I am working on a Phonegap Project where i am at the stage of extending the base capabilities of Phonegap with a custom Plugin. The stupid problem i am having is getting the Plugin to react correctly. The Plugin if called with the 'echo' parameter should answer giving back its matched parameter name, the same goes for 'echo2'.
The strange part:
'echo' returns the expected answer (it executes the success callback) whereas the 'echo2' variant returns the error callback. Seriously running out of ideas...
The JS Definitions: Identical functions (only the 4th Parameter is different)
window.echo = function(str, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to echo.');
}, 'Echo', 'echo', [str]);
};
window.sync = function(str, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to echo.');
}, 'Echo', 'echo2', [str]);
};
The JS Calls on these functions:
echo('Echo String', function(echoValue) {
alert(echoValue);
});
sync('Sync String', function(echoValue) {
alert(echoValue);
});
Java Class:
public class Echo extends CordovaPlugin {
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
switch(action) {
case "echo": String message = args.getString(0);
this.echo("Call on: Echo.echo()" + message, callbackContext);
return true;
case "echo2": String message = args.getString(0);
this.echo("Call on: Echo.echo2()" + message, callbackContext);
return true;
}
return false;
}
private void echo(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
}
To all those having a similar problem, here is some information on why this did not work:
Firstly: The Code works fine - the problem doesn't lie here.
Where's the fault?
When i asked the question, the Java Class was named Echo which worked when the class method was being called. Trying to call any other method fails because the
Phonegap Build Service does not allow direct includes of plugins
BUT in my case it still partially worked because the Java Class Echo happens to be a standard Plugin that Phonegap Build included for me.
This Echo Plugin being included by Phonegap Build happens to have a method echo which resulted in a success callback, obviously.
After further reading:
A tool called plugman (also developed by Adobe) handles the custom plugin implementation by adding the created plugin to the phonegap project ... i am still testing and learning this, the official information (and only information i found) is available here:
► Leads to the deprecated pluginstall tool
► Plugman Tool Repo - GitHub
Related
I'm creating a Nativescript plugin. It includes a custom Android Library (AAR) and I want to use it from the Typescript code. When I run a demo (in device or emulator) I get a TypeError: sender.registerListener is not a function error when calling this registerListener method, which is weird because I'm able to call other methods of the same object.
I think that it could be because I am not implementing properly the interface required as parameter. I think that I can explain it better with code:
Sender.java: the public class I will use in Typescript:
package com.berriart.android.myplugin;
public class Sender {
public static final String TAG = "Sender";
private Context _context = null;
public Sender(Context context) {
_context = context;
}
public void send(final String messagePath, final String messageToSend) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Send call: " + messagePath + " " + messageToSend);
}
}
public void registerListener(MessageListener listener) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "registerListener");
}
}
// Other code here
}
MessageListener.java: the interface that must be implemented by the registerListener parameter:
package com.berriart.android.myplugin;
public interface MessageListener {
void receive(String messagePath, String messageReceived);
}
This is the Typescript (Nativescript) code of the plugin ( to ):
import * as app from "tns-core-modules/application";
export class WearMessaging {
public static send(messagePath: string, messageToSend: string) {
let sender = new com.berriart.android.myplugin.Sender(app.android.context);
sender.send(messagePath, messageToSend);
}
public static registerListener(receiveCallback: (messagePath: string, messageReceived: string) => void) {
let messageListener = new com.berriart.android.myplugin.MessageListener({
receive: receiveCallback
});
let sender = new com.berriart.android.myplugin.Sender(app.android.context);
sender.registerListener(messageListener);
}
}
If I include WearMessaging.send("/demo", "Hola"); in my nativescript application it compiles and run properly, it's call the Java method successfuly. But if I run:
WearMessaging.registerListener((messagePath: string, messageReceived: string) => {
console.log(messagePath);
console.log(messageReceived);
});
The application stops at run time and throws: TypeError: sender.registerListener is not a function refering to the myplugin.android.ts file.
I'm getting crazy trying to make this work, so, let me know if you have any clue. As I say I think that is because I'm missing something when implementing the interface and because the parameter type do not match them method is not being recognized, but maybe I'm wrong.
Here you can see some official doc:
https://docs.nativescript.org/runtimes/android/generator/extend-class-interface
Thanks in advance.
Ok, I solved it :S
It seems that the incremental build was doing something wrong. After deleting manually the build files of the demo everything went fine:
rm -rf platforms/android/build/*
rm -rf platforms/android/app/build/*
# Then build & deploy again
So, question code seems to be fine if you need to do something similar.
I am learning GWT, I am trying following example in which I have tried to pass the JSON object in java function.
public class HomeController implements EntryPoint {
public void onModuleLoad() {
createTestNativeFunction();
Presenter presenter = new PersenterImpl();
presenter.go(RootPanel.get());
}
public native void createTestNativeFunction()/*-{
parser: function() {
var that = this;
var jsonResult = JSON.parse({id:42,name:'yo'});
return this.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
}
void onParse(jsonResult){
System.out.println(jsonResult);
}
}
}-*/;
}
I am getting following errors:
Tracing compile failure path for type 'com.easylearntutorial.gwt.client.HomeController'
[ERROR] Errors in 'file:/C:/Users/ameen/workspace/Tutorial/src/com/easylearntutorial/gwt/client/HomeController.java'
[ERROR] Line 31: missing ; before statement
void onParse(jsonResult){
--------------------------------^
[ERROR] Hint: Check the inheritance chain from your module; it may not be inheriting a required module or a module may not be adding its source path entries properly
[WARN] Server class 'com.google.gwt.dev.shell.jetty.JDBCUnloader' could not be found in the web app, but was found on the system classpath
[WARN] Adding classpath entry 'file:/C:/Program%20Files/gwt-2.7.0/gwt-dev.jar' to the web app classpath for this session
For additional info see: file:/C:/Program%20Files/gwt-2.7.0/doc/helpInfo/webAppClassPath.html
You really should try to avoid JSNI. You can probably write 99% of your code not using JSNI at all. If you really need it, you should use the new JsInterop instead, documentation still in early stage but you can see this documentation here.
If you need to use JsInterop or JSNI it is usually because you need to wrap a JS lib, so first, try to find if it is already wrapped. If it is not you can always use some other wrapper library to learn how to wrap your JS lib.
OpenLayers JsInterop wrapper https://github.com/TDesjardins/gwt-ol3
OpenLayers JSNI wrapper (deprecated) https://github.com/geosdi/GWT-OpenLayers
Or explore github https://github.com/search?q=topic%3Agwt+topic%3Ajsinterop
System.out.println() is a java function, you are looking for console.log().
The body of the native is JavaScript, not Java.
You are declare you variable jsonResult into your parser: function(), jsonResult only exist into that function. Thats why the system say you that
missing ; before statement
Because you never declare the varieble into createTestNativeFunction().
Plus sjakubowski is right System.out.println() is a java function, you need to use console.log() on JavaScript.
Try this:
public native void createTestNativeFunction(){
var jsonResult = {};
parser: function() {
var that = this;
jsonResult = JSON.parse({id:42,name:'yo'});
return this.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
}
void onParse(jsonResult){
console.log(jsonResult);
}
}
I did the following to solve my errors.
public class HomeController implements EntryPoint {
public void onModuleLoad() {
createTestNativeFunction();
Presenter presenter = new PersenterImpl();
presenter.go(RootPanel.get());
}
// var jsonResult = JSON.parse({id:42,name:'yo'});
public native void createTestNativeFunction()/*-{
var that = this;
$wnd.testFunction = function(jsonResult) {
that.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
};
}-*/;
public void onParse(JsObject jsonResult){
int i =42;
}
}
I have a android application (Java) that uses an Android Library.
Everything works fine, but I need to wrap this application into a Xamarin application. So I decided to transform this application into a 2nd Android Library.
I created my Xamarin Android application, and an Android Java Bindings Library project in which I added the two .aar files to the "Jars" folder.
The problems come when I try to compile this binding project. I get a lot of errors like this one :
/.../obj/Release/generated/src/Com.Google.Common.Util.Concurrent.ForwardingListenableFuture.cs(17,17): Error CS0102: The type `Com.Google.Common.Util.Concurrent.ForwardingListenableFutureInvoker' already contains a definition for `id_delegate' (CS0102)
When I open the generated ForwardingListenableFuture.cs file there's this code :
[global::Android.Runtime.Register ("com/google/common/util/concurrent/ForwardingListenableFuture", DoNotGenerateAcw=true)]
internal partial class ForwardingListenableFutureInvoker : ForwardingListenableFuture, global::Com.Google.Common.Util.Concurrent.IListenableFuture {
public ForwardingListenableFutureInvoker (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) {}
protected override global::System.Type ThresholdType {
get { return typeof (ForwardingListenableFutureInvoker); }
}
static IntPtr id_delegate;
// Metadata.xml XPath method reference: path="/api/package[#name='com.google.common.util.concurrent']/class[#name='ForwardingListenableFuture']/method[#name='delegate' and count(parameter)=0]"
[Register ("delegate", "()Lcom/google/common/util/concurrent/ListenableFuture;", "GetDelegateHandler")]
protected override global::Com.Google.Common.Util.Concurrent.IListenableFuture Delegate ()
{
if (id_delegate == IntPtr.Zero)
id_delegate = JNIEnv.GetMethodID (class_ref, "delegate", "()Lcom/google/common/util/concurrent/ListenableFuture;");
return global::Java.Lang.Object.GetObject<global::Com.Google.Common.Util.Concurrent.IListenableFuture> (JNIEnv.CallObjectMethod (Handle, id_delegate), JniHandleOwnership.TransferLocalRef);
}
static IntPtr id_delegate;
// Metadata.xml XPath method reference: path="/api/package[#name='com.google.common.util.concurrent']/class[#name='ForwardingFuture']/method[#name='delegate' and count(parameter)=0]"
[Register ("delegate", "()Ljava/util/concurrent/Future;", "GetDelegateHandler")]
protected override global::Java.Util.Concurrent.IFuture Delegate ()
{
if (id_delegate == IntPtr.Zero)
id_delegate = JNIEnv.GetMethodID (class_ref, "delegate", "()Ljava/util/concurrent/Future;");
return global::Java.Lang.Object.GetObject<global::Java.Util.Concurrent.IFuture> (JNIEnv.CallObjectMethod (Handle, id_delegate), JniHandleOwnership.TransferLocalRef);
}
static IntPtr id_delegate;
// Metadata.xml XPath method reference: path="/api/package[#name='com.google.common.collect']/class[#name='ForwardingObject']/method[#name='delegate' and count(parameter)=0]"
[Register ("delegate", "()Ljava/lang/Object;", "GetDelegateHandler")]
protected override global::Java.Lang.Object Delegate ()
{
if (id_delegate == IntPtr.Zero)
id_delegate = JNIEnv.GetMethodID (class_ref, "delegate", "()Ljava/lang/Object;");
return global::Java.Lang.Object.GetObject<global::Java.Lang.Object> (JNIEnv.CallObjectMethod (Handle, id_delegate), JniHandleOwnership.TransferLocalRef);
}
}
Indeed, the "id_delegate" is duplicated..
I also have warnings like this related to the 1st .aar (not the application one) :
/../JARTOXML: Warning J2X9001: Couldn't load class com/company/project/controllers/activities/MenuActivity : java.lang.NoClassDefFoundError: android/support/v4/app/FragmentActivity (J2X9001)
OK I solved the first part of the errors by adding remove-node statements to the Metadata.xml.
For the 2nd part, adding a reference to Xamarin.Android.Support.v4 solved the problem.
I am creating cordova application witch are showing call logs, so i make plugin and i get calls log in native java code and i don`t know how to pass it back to index.html.
this is my plugin javascript
navigator.callslog= {};
navigator.callslog.show = function () { cordova.exec (null, null, "callslog", "show", []);};
And this is java code for my plugin
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
{
if (action.equals("show")) {
// get call logs
String calls = getCallDetails(callbackContext);
callbackContext.success();
Log.v("Calls", calls);
this.webView.postMessage("callsLog", "show");
} else {
return false;
}
callbackContext.success();
return true;
}
And in index.js i call plugin and data was printed in logcat it works but i don`t know how to get the data and represent in index.html
onDeviceReady: function() {
app.receivedEvent('deviceready');
console.log('Recevedod event ');
var callsLog= navigator.splashscreen.show();
}
Thank you guys
In this first parameter is success call back and second one is fail callback.
instead of null and null
you have to do like this
navigator.callslog.show = function () { cordova.exec (successcb, failcb, "callslog", "show", []);};
function successcb(s){
console.log(s);//what you passed from Java code
}
function failcb(e){
console.log("Err cb");
}
Pass string as an argument for callbackContext.success() method as
callbackContext.success(calls);
I developed a tiny module in Android. When using the debug- or run method in Eclipse to test the app on my "real" device, everything works flawlessly.
Using Eclipse (Kepler), PhoneGap 3.1 and Android API 10
But when I sign, export, install and run the app, I see the following error in the debugger once the plugin is called:
file:///android_asset/www/cordova.js: Line 863 : Uncaught TypeError:
Object org.apache.cordova.al#41ae5438 has no method 'exec'
Uncaught TypeError: Object org.apache.cordova.al#41ae2400 has no
method 'exec' at file:///android_asset/www/cordova.js
I am waiting for the deviceready with a deferred object:
var def_deviceready = $.Deferred();
document.addEventListener("deviceready", deviceready, false);
function deviceready(){
def_deviceready.resolve();
}
function dbaccess(query, arg, callback) {
var dbaccess = cordova.require("cordova/plugin/dbaccess");
$.when(def_deviceready).done(dbaccess.getData(query, arg, callback));
};
dbaccess.js:
cordova.define("cordova/plugin/dbaccess", function (require, exports, module) {
var exec = require("cordova/exec");
module.exports = {
getData: function (query, arg, callback) {
exec(callback, function(){ callback('callbackerror')}, "DBAccess", query, arg);
}
};
});
DBAccess.java:
public class DBAccess extends CordovaPlugin {
HashMap<String, SQLiteDatabase> dbmap;
/**
* Constructor.
*/
public DBAccess() {
dbmap = new HashMap<String, SQLiteDatabase>();
}
#Override
public boolean execute(String action, String arg, CallbackContext callbackContext) throws JSONException {
Log.v("info", "This is what we got here: action=\'" + action +"\', arg=\'"+ arg +"\'.");
if (action != null) {
String Result = getData(action, arg);
this.echo(Result, callbackContext);
return true;
}
return false;
}
.....
.....
...and also the config.xml contains:
<feature name="DBAccess">
<param name="android-package" value="com.phonegap.plugin.dbAccess.DBAccess"/>
</feature>
Any help is greatly appreciated...
your script is not able to include dbaccess.js try to add it forcefully in head tag. thats why its not able to exec the method
I checked the whole project again, thanks to the comment of Vicky (I had the dbaccess.js included...).
I found that for some reason, AppLaud configured my App to run with PhoneGap 3.0, but it was being exported with 2.9, and a different config.xml - hence the inclusion for my module wasn't there at all. I could not figure out where or how the different version/xml file was configured/located in.
So I ended up creating a whole new project, copied my relevant files into the according folders, and now I am up and running!