cocos2d-x Android版にクリップボード コピペ機能実装

cocos2d-x で開発中の関数電卓の Android 版にクリップボードのコピペ(copy&paste)機能をちょいと苦労して実装したので、それを記述しておく。

コピペ機能はフレームワークが用意していてもいいと思うのだが、cocos2d-x はツール開発に使えないことはないもののメインの用途はゲームなので、コピペ機能をサポートしていなくてもいたしかたないところだ。
なので、自分で実装しなくてはいけない。

手順は以下のようになる。

  1. Java で copy, paste を行う関数を実装する
  2. JNI を用いて上記関数をコールするインタフェースを実装する

1. Java で copy, paste を行う関数を実装する

“AppActivity.java”:

public class AppActivity extends Cocos2dxActivity {
    private static ClipboardManager s_cm;
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState); 
        s_cm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
    }
    public static void setClipboardText(String text) {
        s_cm.setText(text);
    }
    public static String getClipboardText() {
        String text = s_cm.getText().toString();
        return text;
    }
}

クリップボードにテキストを設定する関数は setClipboardText(String text) : void, クリップボードに入っているテキストを取得する関数は getClipboardText() : String とした。
クリップボード処理を行う方法はいろいろあるようだが、上記のコードが一番簡単で、動作確認できたので、採用することにした。
クリップボードマネージャを関数内で取得しようとすると、ビルド時にエラーとなってしまったので、onCreate() でスタティック変数に格納したものを参照するようにしている。

2. JNI を用いて上記関数をコールするインタフェースを実装する

“InterfaceJNI.h”:

class InterfaceJNI
{
public:
    static void setClipboardText(const std::string &text);
    static std::string getClipboardText();
};

上記はインタフェースの定義。InterfaceJNI クラスを定義し、set/get 関数をスタティック関数として定義している。
引数・返り値の型は std::string とした。

“InterfaceJNI.cpp”:

#define JNICLASSNAME "org/cocos2dx/cpp/AppActivity"

void InterfaceJNI::setClipboardText(const std::string& text){
    JniMethodInfo methodInfo;
    if(JniHelper::getStaticMethodInfo(methodInfo , JNICLASSNAME , "setClipboardText", "(Ljava/lang/String;)V")){
        jstring str = methodInfo.env->NewStringUTF(text.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID , methodInfo.methodID , str);
        methodInfo.env->DeleteLocalRef(str);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
}
std::string InterfaceJNI::getClipboardText(){
    std::string text = "N/A";
    JniMethodInfo methodInfo;
    if(JniHelper::getStaticMethodInfo(methodInfo , JNICLASSNAME , "getClipboardText", "()Ljava/lang/String;")){
        jstring jstr = (jstring)methodInfo.env->CallStaticObjectMethod (methodInfo.classID , methodInfo.methodID);
        const char* ptr = methodInfo.env->GetStringUTFChars(jstr, NULL);
        text = std::string(ptr);
        methodInfo.env->ReleaseStringUTFChars(jstr, ptr);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
    return text;
}

上記がNJIを用いて Java 関数をコールする部分の実装。
JniMethodInfo methodInfo; を定義し、それの getStaticMethodInfo() 関数を用いて、名前、引数・返り値シグニチャで指定されるメソッドを探す。
メソッドが見つかった場合は CallStaticVoidMethod() または CallStaticObjectMethod() を用いて、見つかったメソッドをコールする。
前者は、std::string を Java の string に変換し、第3引数として渡している。
後者は返ってきたオブジェクトを jstring にキャストし、そこから文字列アドレスを取り出し、std::string に変換し返している。
また、DeleteLocalRef() で不要になったメモリを開放している(?)。