2016年01月06日

Cocos2d-x、Androidでスクリーンショットを付けてTwitter投稿をする

概要

Cocos2d-xでスクリーンショットを付けてTwitter投稿をAndroid側で行う方法についてのメモです。

iOS側の処理についての記事はこちらです。

以下の様な流れで書いていきます。

  1. スクリーンショットを保存する。
  2. JNIでJavaの関数を呼び出すC++の関数を作成する。
  3. AppActivityのonCreateでstaticメンバにthisインスタンスを設定する。
  4. AppActivityにTwitter投稿関数を作成する。
  5. テキストのインテントを作成する処理を追加する。
  6. 画像を外部ストレージに保存する処理を追加する。
  7. 画像をインテントに追加する処理を追加する。
  8. AndroidManifest.xmlに外部ストレージ使用のパーミッションを追加する。
  9. Twitterアプリがインストールされていない場合のエラー処理を追加する。

スクリーンショットを保存する

スクリーンショットを作成する処理はC++の部分で行っているので、iOSと共通です。画像の保存までは少し時間がかかるので、私の場合はちょっと間を置いてからツイートを行う処理を行いました。普通はコールバック関数で終了を受け取ってから処理を行うと思います。

// スクリーンショット用テクスチャを作成する
RenderTexture *texture = RenderTexture::create(width, height);
texture->setPosition(cocos2d::Vector2(width / 2, height / 2));

// スクリーンショットを撮り始める
texture->begin();

// 画面の描画
this->visit();

// スクリーンショットを撮り終える
texture->end();

// スクリーンショットを保存する
texture->saveToFile("screenshot.png", cocos2d::Image::Format::PNG);

JNIでJavaの関数を呼び出すC++の関数を作成する

JNIを使用してJavaの関数を呼び出すC++の関数を作成します。ここではTwitterクラスを作ってstaticメンバ関数としてpostを用意しました。

#include "Twitter.h"
#include <assert.h>
#include <jni.h>
#include "platform/android/jni/JniHelper.h"
#define JNICLASSNAME "org/cocos2dx/cpp/AppActivity"

using cocos2d::JniMethodInfo;
using cocos2d::JniHelper;

// 投稿ビュー表示
void Twitter::post(const char *message, const char *imagepath)
{
    // ネイティブコードのツイート関数を取得する
    JniMethodInfo methodInfo;
    if (!JniHelper::getStaticMethodInfo(methodInfo, JNICLASSNAME, "postTweet", "(Ljava/lang/String;Ljava/lang/String;)V"))
    {
        assert(false);
        return;
    }

    // 文字列をJava Stringに変換する
    jstring message_jstr = methodInfo.env->NewStringUTF(message);
    jstring imagepath_jstr = methodInfo.env->NewStringUTF(imagepath);

    // 関数を呼び出す
    methodInfo.env->CallStaticVoidMethod(methodInfo.classID , methodInfo.methodID , message_jstr, imagepath_jstr);

    // リソースを解放する
    methodInfo.env->DeleteLocalRef(message_jstr);
    methodInfo.env->DeleteLocalRef(imagepath_jstr);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
}

getStaticMethodInfoの引数でJavaの関数名と型を指定します。"(Ljava/lang/String;Ljava/lang/String;)V"の部分はシグネチャというもので、String型の引数が2つあり、戻り値はvoidであることを表すそうです。

なお、このソースをXcodeで作成した場合は、iOSで使用されないようにTarget Membershipから外す必要があります。

AppActivityのonCreateでstaticメンバにthisインスタンスを設定する

ここからはJavaの処理になります。

JNIで使用できるのはstatic関数だけなので、AppActivityのメンバにアクセスするためにstaticメンバ変数にthisインスタンスを入れておきます。

public class AppActivity extends Cocos2dxActivity {

    /** static関数からメンバ関数呼び出すためのthisインスタンス */
    private static AppActivity me = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // 基底クラスの処理を呼び出す
        super.onCreate(savedInstanceState);

        // staticメンバにthisインスタンスを設定する
        me = this;
    }

    ...

AppActivityにTwitter投稿関数を作成する

AppActivityにTwitter投稿用の関数を用意します。public staticにして、関数名と引数はgetStaticMethodInfoで指定したものと同じにします。

    /**
     * Twitter投稿用のインテントを作成し、アクティビティを呼び出す。
     * @param message ツイート内容
     * @param imagePath 画像パス
     */
    public static void postTweet(String message, String imagePath) {

    }

テキストのインテントを作成する処理を追加する

まずはテキストを投稿する部分の処理を作成します。

先ほどのpostTweetに以下の処理を追加します。

        // インテントを作成する。
        Intent intent = new Intent(Intent.ACTION_SEND);

        // インテントにメッセージを追加する
        intent.putExtra(Intent.EXTRA_TEXT, message);

        // パッケージタイプをTwitterにする
        intent.setPackage("com.twitter.android");

        // アクティビティを呼び出す。
        me.startActivity(intent);

画像を外部ストレージに保存する処理を追加する

すでにCocos2d-x側の処理でスクリーンショットを保存しているのですが、この画像ファイルを他のアプリからもアクセスできるように外部ストレージへとコピーします。

    /**
     * パスからファイル名を取得する。
     * @param imagePath 画像ファイルのパス
     * @return 画像ファイルのファイル名
     */
    private static String getFilename(Uri imagePath) {

        // URLの最後のセグメントを返す
        return imagePath.getLastPathSegment();
    }

    /**
     * ファイルを外部ストレージへ保存する。
     * @param imageUri 元画像のURI
     * @return 外部ストレージのURI
     */
    private static Uri saveImageToExternalDirectory(Uri imageUri) {

        // コピー先ファイルのFileインスタンスを作成する
        File dst = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), getFilename(imageUri));

        // コピー元ファイルのFileインスタンスを作成する
        File src = new File(imageUri.getPath());

        try {

            // ファイルをコピーする
            copyFile(src, dst);

        } catch (Exception e) {

            return null;
        }

        // コピー先ファイルからURIを作成して返す
        return Uri.fromFile(dst);
    }

    /**
     * ファイルをコピーする。
     * @param src コピー元ファイル
     * @param dst コピー先ファイル
     * @throws Exception ファイルアクセス異常
     */
    private static void copyFile(File src, File dst) throws Exception {

        // コピー元ファイルの入力ストリームを作成する
        InputStream in = new FileInputStream(src);

        // コピー先ファイルの出力ストリームを作成数r
        OutputStream out = new FileOutputStream(dst);

        // コピー元ファイルの内容を読み込み、コピー先ファイルへ書き込んでいく
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }

        // 入出力ストリームを閉じる
        in.close();
        out.close();
    }

画像をインテントに追加する処理を追加する

先ほどのpostTweet関数に、画像のコピーとインテントへの追加を行う処理を追加します。

    public static void postTweet(String message, String imagePath) {

        ...

        // 画像パスが指定されている場合は画像の処理を行う
        if (!imagePath.equals("")) {

            try {

                // 画像パスからURIを作成する
                Uri originalImageUri = Uri.parse(imagePath);

                // 画像の拡張子を取得する
                String ext = getExtension(originalImageUri);

                // 他のアプリから読み込めるように外部ストレージへと画像をコピーする
                Uri readableFileUri = saveImageToExternalDirectory(originalImageUri);

                // 画像のコピーに成功した場合はインテントに格納する
                if (readableFileUri != null) {
                    intent.setType("image/" + ext);
                    intent.putExtra(Intent.EXTRA_STREAM, readableFileUri);
                }

            } catch (Exception e) {

                // 画像のコピーに失敗した場合は画像なしでツイートを行う
                // ここでは何もしない
            }
        }

        ...

    }

    /**
     * パスからファイルの拡張子を取得する。
     * @param imagePath 画像ファイルのパス
     * @return 画像ファイルの拡張子
     */
    private static String getExtension(Uri imagePath) {

        // URIからパス文字列を取得する
        String pathString = imagePath.getPath();

        // 一番後ろの"."以降の文字列を切り出して返す
        return pathString.substring(pathString.lastIndexOf(".") + 1);
    }

AndroidManifest.xmlに外部ストレージ使用のパーミッションを追加する

外部ストレージに画像をコピーするので、以下のようにAndroidManifest.xmlにパーミッションの記述を追加します。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.monochromesoft.toritoma2"
    android:installLocation="auto">

    ....

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    ...

Twitterアプリがインストールされていない場合のエラー処理を追加する

Twitterアプリが何も入っていない状態の端末で実行するとstartActivityで例外が発生してしまったので、その場合にはTwitterのURLを指定してブラウザを起動するようにインテントを作り直します。

対応するアプリがあるかを調べることができれば良いのですが、やり方がわからなかったのでtry catchで処理しています。

最終的なpostTweet関数は以下の通りです。

    /**
     * Twitter投稿用のインテントを作成し、アクティビティを呼び出す。
     * @param message ツイート内容
     * @param imagePath 画像パス
     */
    public static void postTweet(String message, String imagePath) {

        // インテントを作成する。
        Intent intent = new Intent(Intent.ACTION_SEND);

        // インテントにメッセージを追加する
        intent.putExtra(Intent.EXTRA_TEXT, message);

        // パッケージタイプをTwitterにする
        intent.setPackage(TWITTER_PACKAGE_NAME);

        // 画像パスが指定されている場合は画像の処理を行う
        if (!imagePath.equals("")) {

            try {

                // 画像パスからURIを作成する
                Uri originalImageUri = Uri.parse(imagePath);

                // 画像の拡張子を取得する
                String ext = getExtension(originalImageUri);

                // 他のアプリから読み込めるように外部ストレージへと画像をコピーする
                Uri readableFileUri = saveImageToExternalDirectory(originalImageUri);

                // 画像のコピーに成功した場合はインテントに格納する
                if (readableFileUri != null) {
                    intent.setType("image/" + ext);
                    intent.putExtra(Intent.EXTRA_STREAM, readableFileUri);
                }

            } catch (Exception e) {

                // 画像のコピーに失敗した場合は画像なしでツイートを行う
                // ここでは何もしない
            }
        }

        try {

            // アクティビティを呼び出す。
            me.startActivity(intent);

        } catch (ActivityNotFoundException e) {

            // Twitterクライアントがない場合は代わりにブラウザでTwitterのサイトを開くようにする。
            // 画像を付けることはできないため、テキストメッセージだけを付ける。

            // Twitter投稿用インテントに設定するURLを作成する。
            // TwitterのURLのパラメータにメッセージを設定する。
            // ハッシュタグの#の文字はそのままでは処理できないため、URLエンコードの%23に置換する。
            String twitterUrl = String.format("http://twitter.com/share?text=%s", message).replaceAll("#", "%23");

            // ブラウザ表示用のインテントを作成する
            Intent intentForBrowser = new Intent(Intent.ACTION_VIEW, Uri.parse(twitterUrl));

            // アクティビティを呼び出す。
            me.startActivity(intentForBrowser);
        }
    }

参考Webサイト

cocos2d-x向けの究極のソーシャル連携プラグイン作った - 5.1さらうどん

ラベル:cocos2d android
posted by かねだ at 06:29| Comment(0) | 開発方法メモ | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。