Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
257 views
in Technique[技术] by (71.8m points)

javascript - Custom cordova plugin creation for ionic2 project

Many of us would have gone through similar issues, but even after going through following most relevant links reference link1 and reference link2 , I am not able to resolve.

Issue:

Create a custom plugin (Cordova) in-order to use this in ionic2 project.

Expectation: This plugin will be able to interact with native features of IOS and Android. To be precise I am trying to access features of a native SDK (Aruba Internal Positioning SDK) using cordova into Ionic project.

Step 1 Initially created plugin as per reference link 1

Step 2 Created Ionic 2 project( created with this basic steps )

Step 3 JavaScript file in plugin was not able to refer and access in Ionic2 .

After googling , I found this discussion , where it is told to create interface in plugin itself because of the following reason.

import {myPluginName} from '../../../plugins/xxx/*.js'

Will not work because the plugin is not part of the ionic native bundle.

If you have a custom plugin, you can do a few things.

1) Make a PR to add it to ionic-native proper

2) Use the raw plugin API. You can use the raw plugin API without having it be part of Ionic Native. The plugin is on the window object, so you would target the api normally

window.plugin.myPlugin.myMethod()

According to the GITHUB Example project this way the interface should be implemented

interface CordovaPlugins {
  ZPLPrinter: ZPLPrinter;
}

interface ZPLPrinter {

  print(
    ipaddress: string,
    bclabels: any,
    printSuccess: (ip: string, labels: string[]) => void,
    printError: (message: string) => void): void;

}

Now I created a similar interface in my plugin which is the following in plugin's www folder

interface CordovaPlugins {
  Communicator: Communicator;
}

interface Communicator {

  getInfo(successCallback: any, errorCallback: any);

}

This interface would ideally target this method in JS file

Device.prototype.getInfo = function(successCallback, errorCallback) {
    console.log("device.js: getInfo function called");
    argscheck.checkArgs('fF', 'Device.getInfo', arguments);
    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};

Now I am stuck , as my Ionic project is not having typings folder itself.

In the sample Github Project, cordova packages are referred using typings folder . TypeScript File in project is referring Cordova using index.t.js

Import used to refer should go like

declare var cordova: Cordova;

Doubts:

  1. Am I in the wright direction of the process
  2. Is this the way to create Cordova plugin and use in ionic
  3. Why I am not able to get typings folder in Ionic2

EDIT 1:

After just adding the plugin without even referring in Ionic project, I tried to run in Android device. But it gave me the following error.

Main error is this

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ionicframework.cutepuppypics234138/com.ionicframework.cutepuppypics234138.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference

Why would this error be causing? Detailed logs have given below

12-08 16:10:49.079 20555-20555/? E/ApkAssets: Error while loading asset assets/natives_blob_64.bin: java.io.FileNotFoundException: assets/natives_blob_64.bin
12-08 16:10:49.079 20555-20555/? E/ApkAssets: Error while loading asset assets/snapshot_blob_64.bin: java.io.FileNotFoundException: assets/snapshot_blob_64.bin
12-08 16:10:49.682 20555-20555/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ionicframework.cutepuppypics234138, PID: 20555
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ionicframework.cutepuppypics234138/com.ionicframework.cutepuppypics234138.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2339)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2413)
   at android.app.ActivityThread.access$800(ActivityThread.java:155)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1317)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5343)
   at java.lang.reflect.Method.invoke(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void org.apache.cordova.CordovaPlugin.privateInitialize(java.lang.String, org.apache.cordova.CordovaInterface, org.apache.cordova.CordovaWebView, org.apache.cordova.CordovaPreferences)' on a null object reference
   at org.apache.cordova.PluginManager.getPlugin(PluginManager.java:171)
   at org.apache.cordova.PluginManager.startupPlugins(PluginManager.java:97)
   at org.apache.cordova.PluginManager.init(PluginManager.java:86)
   at org.apache.cordova.CordovaWebViewImpl.init(CordovaWebViewImpl.java:115)
   at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149)
   at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:224)
   at com.ionicframework.cutepuppypics234138.MainActivity.onCreate(MainActivity.java:39)
   at android.app.Activity.performCreate(Activity.java:6010)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2413)?
   at android.app.ActivityThread.access$800(ActivityThread.java:155)?
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1317)?
   at android.os.Handler.dispatchMessage(Handler.java:102)?
   at android.os.Looper.loop(Looper.java:135)?
   at android.app.ActivityThread.main(ActivityThread.java:5343)?
   at java.lang.reflect.Method.invoke(Native Method)?
   at java.lang.reflect.Method.invoke(Method.java:372)?
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)?
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)?
12-08 16:10:49.879 20656-20656/? E/SubDex: SubDex Config : .dex 2
12-08 16:10:50.285 20656-20656/? E/project: extsdcard==/storage/emulated/0/Android/data/com.cleanmaster.mguard/files
12-08 16:10:50.303 20656-20656/? E/project: extsdcard==/storage/emulated/0/Android/data/com.cleanmaster.mguard/files
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

After many trial and errors I found the solution.

I am putting down below details for future reference to any one who is trying a similar stuff!

Issues with code was as follows (my plugin name is Inject)

  • InjectwwwInject.js was not having essential function for installing the plugin
  • Any method name mentioned in Inject.js should be same till InjectsrcInject.java as there are options in execute method to refer different method name based on tag's received

Steps:

  1. Use plugman to create skeleton of plugin Reference Link
  2. Instead of using names like cordova-plugin-am-i-late , use cordova.plugin.Inject . I faced compile/run time issues using - symbol
  3. Add targeted platform plugman platform add --platform_name android
  4. Verify plugin.xml comparing the same reference
  5. Include permissions in plugin.xml if required
  6. Now verify Inject.js its missing essential method calls. Currently its having only the following code.

var exec = require('cordova/exec');
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "Inject", "coolMethod", [arg0]);
};
  1. But we need to include below also to make it install and work

function Inject(){
}
Inject.install = function () {
  if (!window.plugins) {
    window.plugins = {};
  }

  window.plugins.Inject = new Inject();
  return window.plugins.Inject;
};

cordova.addConstructor(Inject.install);
  1. For example I am targeting to show a Toast message for which below is my complete Inject.js file

function Inject(){
}

Inject.prototype.coolMethod = function (options, successCallback, errorCallback) {
  cordova.exec(successCallback, errorCallback, "Inject", "coolMethod", []);
};

Inject.install = function () {
  if (!window.plugins) {
    window.plugins = {};
  }

  window.plugins.Inject = new Inject();
  return window.plugins.Inject;
};

cordova.addConstructor(Inject.install);
  1. Now lets implement our Inject.java

public class Inject extends CordovaPlugin {

private static final int GRAVITY_CENTER = Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL;
private static final String TAG = "InjectCordovaPlugin";
String messageReceived;

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        Log.v(TAG, "execute , action =" + action);
        if (action.equals("coolMethod")) {
            String message = args.getString(0);
            Log.v(TAG, "coolMethod called with message =" + message);
            this.coolMethod(message, callbackContext);
            return true;
        }

        return false;
    }
    private void coolMethod(String message, CallbackContext callbackContext) {
    Log.v(TAG, "Inject's coolMethod called ,message="+message);
    messageReceived = message;
        if (message != null && message.length() > 0) {
            cordova.getActivity().runOnUiThread(new Runnable() {
            public void run() {

            final android.widget.Toast toast = android.widget.Toast.makeText(
              cordova.getActivity().getWindow().getContext(),
              messageReceived,
              android.widget.Toast.LENGTH_LONG 
                );
                toast.setGravity(GRAVITY_CENTER, 0, 0);
                toast.show();
            }
            });
            callbackContext.success(message);
        } else {
            callbackContext.error("Expected one non-empty string argument.");
        }
    }
}
  1. Just to make this clear I'm putting out my final plugin.xml

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova.plugin.Inject" 
    version="1" 
    xmlns="http://apache.org/cordova/ns/plugins/1.0" 
    xmlns:android="http://schemas.android.com/apk/res/android">
<name>Inject</name>
<js-module name="Inject" src="www/Inject.js">
    <clobbers target="window.plugins.Inject"/>
</js-module>
<platform name="android">
<config-file parent="/*" target="res/xml/config.xml">
<feature name="Inject">
<param name="android-package" 
        value="cordova.plugin.Inject.Inject" />
        </feature>
</config-file>
<config-file parent="/*" target="AndroidManifest.xml">
</config-file>
<source-file src="src/android/Inject.java" 
target-dir="src/cordova.plugin.Inject/Inject" />
</platform>
</plugin>
  1. Now create a sample Ionic2 project and add Android/IOS platform
  2. Add the created plugin using cordova plugin add command
  3. Navigate to ionic project in Nodejs command prompt and give this command (refer Plugin folder base after add ) cordova plugin add D:PluginTrialInject
  4. The added plugin should be populated under Ionic2Projectplugins folder
  5. Call this function using window object. Below is my home.ts

import { Component } from '@angular/core';

import { NavController, Platform } from 'ionic-angular';

declare var window: any;
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, private platform: Platform) {

  }
    showToast(message, position) {
        this.platform.ready().then(() => {
            window.plugins.Inject.coolMethod(message, "short", position);
        });
    }
}
  1. Now refer this showToast method in home.html file

<button ion-button (click)="showToast('Yo Man! Its working ', 'center')">Default</button>
  1. That's it. You should be able to test the plugin successfully! Happy Coding

Reference: Reference One , Reference Two , Reference Three


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...