Java
Objective-C
Android
PhoneGap
iOS

PhoneGapプラグインの作成

More than 3 years have passed since last update.

公式のドキュメントが少なすぎて使えないので書いてみる。

使用した環境

MacOSX Mavericks
PhoneGap 3.1 (npmでインストール)
Eclipse
Xcode
zsh (ターミナル)

まずプラグインを作成するための準備から

javascript

plugin.js
window.plugin = {
    hoge: {
        fuga : function() {
            cordova.exec(function(message) {
                    // success callback
                },
                function(message) {
                    // error callback
                },
                'hoge', 'fuga', [args]);
    }
};

cordova.execでプラグインのメソッドを実行する。
ここではオブジェクトにまとめているだけ。
実行するのはhogeクラスのfugaメソッド。

Androidでの設定

Project/platforms/android/res/xml/config.xml
<widget id=“”com.fuga.hoge” version=”2.0.0” xmlns=“http://www.w3org/ns/widgets”>
<!— ----------中略---------- —>
    <feature name=“hoge”>
        <param name=“android-package” value=”com.plugin.pack.hoge”>
    </feature>
<!— ----------中略---------- —>
</widget>

config.xml内で使用するプラグインの情報を指定する。
platform/androidがなければまずビルドしましょう。
featureのname属性で指定した名前でJavaScriptから呼び出せますが、
混乱を避けるためにクラス名を一緒にしています。
paramのvalue属性は完全修飾でクラス名を指定しています。

iOSでの設定

Project/platforms/ios/ProjectName/config.xml
<widget id=“”com.fuga.hoge” version=”2.0.0” xmlns=“http://www.w3org/ns/widgets”>
<!— ----------中略---------- —>
    <feature name=“hoge”>
        <param name=“ios-package” value=”hoge”>
    </feature>
<!— ----------中略---------- —>
</widget>

特に難しいこともなくandroid-packageからios-packageへ変更
クラス名の名前空間がなくなったくらい。
もしかするとProject/www/config.xmlにまとめて記述してもいいのかもしれないですが試してません。

以上で設定は終了。

やっとPluginの作成へ

Android

Project/platforms/android/src/com/plugin/pack/hoge.java
package com.plugin.pack;

import apache.cordova.CordovaPlugin;
import apache.cordova.CallbackContext;
import apache.cordova.PluginResullt;

public class hoge extends CordovaPlugin {
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
        if (action.equals(hoge)) {
            this.hoge();

            // 引数データの取得
            String param = args.getString(0);

            // Javascriptのコールバックへデータを送る。
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, “成功したよ!”);
            // エラーの場合
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.Error, “エラー発生!”);
            return true;
        }
        return false;
    }
    private void hoge() {
        // いろいろと処理
        // 表示しているActivityの取得
        Activity activity = super.cordova.getActivity(); // 取得したらあとはお好きにどうぞ。
        //WebView 取得
        WebView webView = super.webView; //あとは好きに(ry
    }
}

まずプラグインのメソッドが呼び出されるとexecuteメソッドが呼び出される。
JavaScriptからcordova.execを呼んだ際の第4引数がStringに、
第5引数がJsonArrayに落ちてくる。
ので、まずActionで処理を振り分ける。

Activityを取得したければcordova.getActivity
WebViewを取得したければwebViewにアクセスすればいい。
WebViewCordovaWebViewというWebViewを拡張したもの返される。
おそらく通常のWebViewと同様に扱っていいはず。。

好きなように処理をした後、callbackContext.sendPluginResult
を呼ぶとJavaScript側で設定したコールバック関数が呼び出される。

executeの返り値はactionが該当しなかった場合falseを返せば良い。(たぶん

iOS

Project/platforms/ios/ProjectName/Plugins/hoge.h
#import <Cordova/CDVPlugin.h>
#import “Foundation/Foundation.h”

@interface hoge : CDVPlugin

- (void) fuga : (CDVInvokeUrlCommand*) command;

@end

ヘッダはimport継承をするだけ。

Project/platforms/ios/ProjectName/Plugins/hoge.m
#import “Cordova/CDV.h”
#import “hoge.h”

@implemention hoge

- (void) fuga : (CDVInvokeUrlCommand*) command {

    // UIViewController取得
    UIViewController *uiViewController = super.viewController;

    // UIWebView取得
    UIWebView webView = super.webView;

    // JavaScript側からの引数取得
    String arg = [command argumentAtIndex: 0];

    // JavaScriptのコールバックへデータを送る
    // 成功の場合
    CDVPluginResult plutinResult =[CDVPluginResult resultWithStatus: CDVCommandStatus_OK messageAsString: @“成功だよ!”];
    // エラーの場合
    CDVPluginResult plutinResult =[CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsString: @“エラー発生!”];

    // データ送信
    [super.commandDelegate sendPluginResult: pluginResult callbackId: command.callbackId];
}

@end

iOSは単純で、JavaScript側の第3引数の名前のクラス、第4引数の名前のメソッドが呼び出されるので、そこで処理を行えば良い。

必要なデータの取得等はコメントの通り。

注意点

処理をブロッキングすると当然ながらUIが止まります。
ですが、Androidの場合はおそらく引数のCallbackContextの内容が書き換わっているためか、
executeメソッドが終了するとJavaScriptのコールバックが呼び出せなくなります。
iOSでは未確認です。

なので、非同期処理を行いたい場合はJavaScript側で設定したコールバックを呼び出すことが出来ませんでした。

できる方がいたら教えて下さい。

今回AndroidはGenyMotionのエミュレータで動作確認をしましたが、
PhoneGapコマンドラインツールでのビルドとEclipseでのビルドでリテラル文字列の文字コードが変わりました。
ツールに問題があるのかGenyMotionに問題があるか微妙なところですが、お気をつけください。