Custom cordova plugin creation for ionic2 project

前端 未结 3 837
走了就别回头了
走了就别回头了 2021-01-01 01:41

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.

相关标签:
3条回答
  • 2021-01-01 02:03

    As far as typings is concerned, it is no longer used. All or most typescript declarations are moved to npm itself and you install them as npm install @types/package_name. https://www.npmjs.com/~types https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md If you need typings folder you could try

    npm install typings
    

    you can also referance type declararions through

    // <reference path="" />
    

    in typescript

    0 讨论(0)
  • 2021-01-01 02:11

    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)

    • Inject\www\Inject.js was not having essential function for installing the plugin
    • Any method name mentioned in Inject.js should be same till Inject\src\Inject.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:\PluginTrial\Inject
    4. The added plugin should be populated under Ionic2Project\plugins 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

    0 讨论(0)
  • 2021-01-01 02:29

    Your plugin needs to look like this:

    In: /[custom plugin name]/js/custom_plugin.js

    var CustomPlugin = function(){};
    
    CustomPlugin.someFunction = function(){
        console.log("someFunction starts");
    
        return new Promise(function(resolve,reject){
        cordova.exec(
            resolve,
            reject,
            [PLUGIN_NAME],
            [ACTION_ON_NATIVE_SIDE],
            []
        );
    
        });
        console.log("someFunction stops");
    }
    
    .... more functions
    
    module.exports = CustomPlugin;
    

    In: /[custom plugin name]/src/[android]||[ios] , the classes with native code.

    And in: /[custom plugin name]/plugin.xml (this is an example, settings have to be adjusted to your case):

    <?xml version="1.0" encoding="UTF-8"?>
    <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
        id="[CustomPlugin]"
        version="1.0.0">
        <name>CustomPlugin</name>
        <description>...</description>
        <license>...</license>
        <author>...</author>
    
        <engines>
            <engine name="cordova" version=">=6.0.0" />
        </engines>
    
    
    
        <js-module src="www/js/custom_plugin.js" name="CustomPlugin">
            <clobbers target="CustomPlugin" />
        </js-module>
    
    
        <platform name="ios">
            <config-file target="config.xml" parent="/*">
                <preference name="orientation" value="portrait"/>
                <feature name="CustomPlugin">
                    <param name="ios-package" value="CustomPlugin" />
                    <param name="onload" value="true"/>
                </feature>
            </config-file>
    
            <header-file src="src/ios/CustomPlugin.h" />
            <source-file src="src/ios/CustomPlugin.m" />
            <!--framework src="QuartzCore.framework" /> 
            <framework src="AssetsLibrary.framework" />
            <framework src="CoreGraphics.framework" />
            <framework src="MobileCoreServices.framework" /-->
        </platform>
    
        <platform name="android">
            <config-file target="res/xml/config.xml" parent="widget">
                <preference name="orientation" value="portrait"/>
                <feature name="CustomPlugin" >
                    <param name="android-package" value="[package name].CustomPlugin"/>
                    <param name="onload" value="true"/>
                </feature>
            </config-file>
    
            <config-file target="AndroidManifest.xml" parent="/*">
                <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
                <uses-permission android:name="..." />
                <uses-feature android:name="..." />
            </config-file>
    
            <source-file src="src/android/CustomPlugin.java" target-dir="[package folder directory organization like: com.android.something]" />
            <source-file ... />
            <source-file src="src/android/custom_plugin.xml" target-dir="res/layout" />
    
        </platform>
    
    </plugin>
    

    then you add you plugin with the CLI: ionic plugin add [folder of your plugin]

    In your Ionic project, in the classes (angular2 directives) where you want to use your plugin, write before the @Component section: declare var CustomPlugin: any;. Then in that class, you can use your plugin by refering to CustomPlugin that is exported with module.exports = CustomPlugin; from the file: /[custom plugin name]/js/custom_plugin.js.

    TO ANSWER EDIT 1 OF THE QUESTION, HERE SOME DETAILS OF THE ANDROID PART: In the android plugin project (once platform android has been added and built at least once, with ionic CLI), in android studio (2.2.2), when looking at the build project under "[my project]\platforms\android":

    In the hierarchy, the MainActivity file is autogenerated under: "android\java\com\ionicframework.[my project name + a large number]\MainActivity":

      package com.ionicframework.[my project name + a large number];
    
    import android.os.Bundle;
    import org.apache.cordova.*;
    
    public class MainActivity extends CordovaActivity
    {
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
    
            // enable Cordova apps to be started in the background
            Bundle extras = getIntent().getExtras();
            if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
                moveTaskToBack(true);
            }
    
            // Set by <content src="index.html" /> in config.xml
            loadUrl(launchUrl);
        }
    }
    

    For my custom plugin (not going into details here) under "android\java[package of the custom plugin]:

    package [package of the custom plugin];
    
    import org.apache.cordova.CallbackContext;
    import org.apache.cordova.CordovaPlugin;
    import org.apache.cordova.PluginResult;
    
    // + other imports needed
    
    public class CustomPlugin extends CordovaPlugin  {
    
        private static CallbackContext customDeferredCallback;
    
        public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
    //all the thing corresponding to your case need to end if with either:
    //   if OK: callbackContext.success(); return true;  ;
    //   if not OK: callbackContext.error(error.getMessage()); return false;
    //   if JAVA returns a result to JS do: actionBindListener(callbackContext);
    
    
    
    }
    
        private boolean actionBindListener(final CallbackContext callbackContext){
        cordova.getThreadPool().execute(new Runnable() {
                public void run(){
                    try{
                        customDeferredCallback= callbackContext;
                    }catch(Exception e){e.printStackTrace();}
                }
            });
            return true;
        }
    
    
        //in your program when you get the result you want to send back to javascript call this function
        public static void sendResultToJS(res){
            JSONObject eventData = new JSONObject();
            try {
                eventData.put("CUSTOM_KEY", res);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventData);
            pluginResult.setKeepCallback(true);
            try{
                    customDeferredCallback.sendPluginResult(pluginResult);
            } catch(NullPointerException e){
                e.printStackTrace();
            }
        }
    }
    

    And finally android\manifests\manifests.xml looks like that:

    <?xml version='1.0' encoding='utf-8'?>
    <manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" package="com.ionicframework.[project name + large number]" xmlns:android="http://schemas.android.com/apk/res/android">
        <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
        <uses-permission android:name="android.permission.INTERNET" />
        <application android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:supportsRtl="true">
            <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
                <intent-filter android:label="@string/launcher_name">
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity android:exported="true" android:name="com.adobe.phonegap.push.PushHandlerActivity" />
            <receiver android:name="com.adobe.phonegap.push.BackgroundActionButtonHandler" />
            <receiver android:exported="true" android:name="com.google.android.gms.gcm.GcmReceiver" android:permission="com.google.android.c2dm.permission.SEND">
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="${applicationId}" />
                </intent-filter>
            </receiver>
            <service android:exported="false" android:name="com.adobe.phonegap.push.GCMIntentService">
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                </intent-filter>
            </service>
            <service android:exported="false" android:name="com.adobe.phonegap.push.PushInstanceIDListenerService">
                <intent-filter>
                    <action android:name="com.google.android.gms.iid.InstanceID" />
                </intent-filter>
            </service>
            <service android:exported="false" android:name="com.adobe.phonegap.push.RegistrationIntentService" />
        </application>
        <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="24" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.VIBRATE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
        <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
        <permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    </manifest>
    
    0 讨论(0)
提交回复
热议问题