Cocos2d-xで撮ったスクリーンショットをTwitterに添付してツイートできないか試してみた

Cocos2d-x上からTwitterにツイートする - おかひろの雑記ではテキストデータのみのツイートをやってみましたが、
Cocos2d-xの画面のスクリーンショットを添付してツイートできないか試してみました。


ちなみにCocos2d-xでスクリーンショットを撮る方法はこちらhttp://www.cocos2d-x.org/projects/cocos2d-x/wiki/How_to_save_a_screenshotを参考にしました。

処理の流れ
1.Cocos2d-X上でスクリーンショットを撮って、ファイルに保存
 ↓
2.保存したファイルのパスを取得して、ネイティブに渡す
 ↓
3.ネイティブはパスから画像を取得し、ツイートする

とりあえずこういう流れでやってみました。

今回使用したバージョン

  • Cocos2d-X 2.1.4

スクリーンショット保存
AppDelegateにstaticメソッドとして実装しました。

AppDelegate.h

static void saveScreenShot();

AppDelegate.cpp

void AppDelegate::saveScreenShot()
{
        CCSize size = CCDirector::sharedDirector()->getWinSize();
        CCRenderTexture* texture = CCRenderTexture::create((int)size.width, (int)size.height);
        texture->setPosition(ccp(size.width * 0.5f, size.height * 0.5f));
        texture->begin();
        CCDirector::sharedDirector()->getRunningScene()->visit();
        texture->end();
        texture->saveToFile("screenshot.jpg", kCCImageFormatJPEG);
}

ネイティブ連携
ツイートする内容に加え、画像ファイルのフルパスを一緒に渡すようにしています。

共通
NativeCodeLauncher.h

namespace Cocos2dExt {
    class NativeCodeLauncher
    {
    public:
		static void openTweetDialog(char const *tweet,char const *filePath);
    };
}

iOS
NativeCodeLauncher.mm

static void static_openTweetDialog(const char* tweet,const char* filePath)
{
        [NativeCodeLauncher openTweetDialog:[NSString stringWithUTF8String:tweet] filePath:[NSString stringWithUTF8String:filePath]];
}

namespace Cocos2dExt
{
        void NativeCodeLauncher::openTweetDialog(const char *tweet,const char *filePath)
        {
                static_openTweetDialog(tweet,filePath);
        }
}

NativeCodeLauncher_objc.h

@interface NativeCodeLauncher : NSObject

+(void)openTweetDialog:(NSString *)tweet filePath : (NSString *)filePath;

@end

NativeCodeLauncher_objc.m

@implementation NativeCodeLauncher

+(void)openTweetDialog:(NSString *)tweet filePath:(NSString *)filePath
{
        if([TWTweetComposeViewController canSendTweet])
        {
                AppController *appController = (AppController *)[UIApplication sharedApplication].delegate;
                TWTweetComposeViewController *tweetController = [[TWTweetComposeViewController alloc]init];
                [tweetController setInitialText:tweet];
                
                UIImage *image = [UIImage imageWithContentsOfFile:filePath];
                [tweetController addImage:image];
                
                [appController.viewController presentModalViewController:tweetController animated:YES];
        }
        else
        {
                tweet = [NSString stringWithFormat:@"http://twitter.com/home?status=%@",tweet];
                tweet = [tweet stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
                NSURL *url = [NSURL URLWithString:tweet];
                [[UIApplication sharedApplication] openURL:url];
        }
}

@end

iOSの場合は画像ファイルをUIImageに読み込むだけなので簡単です。


Android
NativeCodeLauncher.cpp

namespace Cocos2dExt
{
        void NativeCodeLauncher::openTweetDialog(char const* tweet,char const *filePath)
        {
                openTweetDialogJNI(tweet,filePath);
        }
}

NativeCodeLauncherJni.h

extern "C"
{
	extern void openTweetDialogJNI(char const* tweet,char const *filePath);
}

NativeCodeLauncherJni.cpp

...

//#define  CLASS_NAME "org/cocos2dx/lib/Cocos2dxActivity"
#define CLASS_NAME "jp/milt/okahiro/OkahiroCocos2dX"

...

extern "C"
{

...

        void openTweetDialogJNI(char const* tweet,char const *filePath)
        {
                JniMethodInfo methodInfo;
        
                if (!getStaticMethodInfo(methodInfo, "openTweetDialog", "(Ljava/lang/String;Ljava/lang/String;)V"))
                {
                    return;
                }
        
                jstring tweetArg = methodInfo.env->NewStringUTF(tweet);
                jstring filePathArg = methodInfo.env->NewStringUTF(filePath);
                methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, tweetArg,filePathArg);
                methodInfo.env->DeleteLocalRef(tweetArg);
                methodInfo.env->DeleteLocalRef(filePathArg);
                methodInfo.env->DeleteLocalRef(methodInfo.classID);
        }
}

OkahiroCocos2dX.java

public class OkahiroCocos2dX extends Cocos2dxActivity{

	private static Activity me = null;
	
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		
		me = this;
	}
	
    static {
         System.loadLibrary("game");
    }
    
    // JNIから呼び出すメソッド
    public static void openTweetDialog(String tweet,String filePath)
    {
        final String path = filePath;
        final String tweetMessage = tweet; 
        
        me.runOnUiThread(new Runnable(){
                @Override
                public void run()
                {
                        File f = new File(path);
                        
                        byte[] data;
                                try
                                {
                                        data = readFileToByte(path);
                                }
                                catch(Exception e)
                                {
                                        Log.e("Debug", e.getMessage());
                                        return;
                                }
                                
                                File savePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
                                File saveFile = new File(savePath,"screenshot.jpeg");
                                
                                if(!savePath.exists())
                                {
                                        savePath.mkdir();
                                }
                                
                                FileOutputStream fos = null;
                                
                                try
                                {
                                        fos = new FileOutputStream(saveFile);
                                        fos.write(data);
                                        fos.close();
                                }
                                catch(Exception e)
                                {
                                        Log.e("Debug", e.getMessage());
                                        return ;
                                }
                        
                                Uri uri = Uri.fromFile(saveFile);
                
                                Intent it = new Intent(Intent.ACTION_SEND);
                                it.putExtra(Intent.EXTRA_SUBJECT, "");
                                it.putExtra(Intent.EXTRA_TEXT, tweetMessage);
                                it.putExtra(Intent.EXTRA_STREAM,uri);
                                it.setType("image/jpeg");
               
                                me.startActivity(Intent.createChooser(it,"共有"));
                }
        });
    }

    private static byte[] readFileToByte(String filePath) throws Exception
    {
                byte[] b = new byte[1];
                FileInputStream fis = new FileInputStream(filePath);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                while (fis.read(b) > 0) {
                        baos.write(b);
                }
                baos.close();
                fis.close();
                b = baos.toByteArray();
        
                return b;
    }
}

Androidの場合は自分の知識不足もあり、いろいろと苦労しました。
画像ファイルをUri化してIntentで渡すだけだとAndroid4.2でうまくいかなかったので、画像ファイルを外部ストレージに一旦コピーしています。
そのため、AndroidManifest.xmlパーミッションが必要になります。

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


呼び出し

        // スクリーンショット保存
        AppDelegate::saveScreenShot();
        // パスを取得
        std::string filePathName = CCFileUtils::sharedFileUtils()->getWritablePath().append("screenshot.jpg");
        // ネイティブに渡す
        Cocos2dExt::NativeCodeLauncher::openTweetDialog("テストツイート http://example.com/ #テスト",filePathName.c_str());

スクリーンショット画像ファイルの保存先はCCFileUtilsクラスのgetWritablePathメソッドで取得しますが、
このパスがCocos2d-Xのバージョンによって変わっていることもあるようなので、少なくともiOSAndroid
どういうパスが指定されているのか、ログなどで確認をしておいたほうがいいと思います。


結果

スクリーンショット画像はCocos2d-X上で作成しているので、広告などは映りません。

iOS

Android twicca

Android tweecha

Android 公式twitter



とりあえず作って動かしてみたレベルのものなので、ネイティブの特にAndroidのソースに関しては、
課題が残っている感じもします。