Share a screenshot with social share (Cocos creator)

Hello everyone!
I have already found some posts to make it, but it didn’t work for me. I want to share score when you finish a level, so I have to take a screenshot, save it, and upload it in facebook/twitter or whatever the players want.

It’s for android and iOS, so I prefer a solution who works for both of them. For now, screenshot works on the cocos creator simulator, and share without a picture works on phone. I don’t know if the screenshot works on phone because the path where the picture is saved can’t be read… But the error I have in the log is:

open failed: EACCES (Permission denied)

whereas I have
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
in the android manifest

Here is my actual code:

    onClick: function () {
var fileName = "screenshot.png"
        var tex = new cc.RenderTexture(this.winSize.width, this.winSize.height, cc.Texture2D.PIXEL_FORMAT_RGBA8888);
        tex.setPosition(cc.p(this.winSize.width / 2, this.winSize.height / 2));
        tex.setAutoDraw(true);
        tex.begin();
        cc.director.getRunningScene().visit();
        tex.end();

        var imgPath = jsb.fileUtils.getWritablePath();
        if (imgPath.length == 0) {
            return;
        }

        var saved = function () {

            var shareInfo = {};
            shareInfo.text = "This new game is HOT!";
            shareInfo.title = "Hottiest";
            shareInfo.image = imgPath+fileName;
            shareInfo.link = "http://abyssal-studio.com";
            sdkbox.PluginShare.nativeShare(shareInfo);
        };

        var result = tex.saveToFile(fileName, cc.IMAGE_FORMAT_PNG, true, saved);
        if (result) {
            imgPath += fileName;
  }      
}

Maybe it’s because I need to ask for runtime permission ? But I have no idea how to do that with cocos creator…

I think you need both read and write permission

I think you have the same issue as: SDKBox Share add image to email not working in Android

if you find a clean way to save the png so it can be shared to different apps let me know. I’m using javascript so not sure how to invoke java calls to get the other file paths…

I already have write and read permission in my manifest

Yes that is this issue. I’m using javascript too and I have no idea how to use native function… So I’m lost to make what I want, if someone can help I will be really thankful!
I would like to save the screenshot in a gallery folder, but I have no idea about how to make it…

Ok, so I have edited cocos2dxHelper.java in order to write my image in a public directory. It seems to works (I have the good path when I call “jsb.fileUtils.getWritablePath();”, but I have nothing saved, no folder created, no picture, nothing…

I have an other problem in the logs, but I don’t understand what it means:

02-15 16:12:24.321  5361  5361 W Bundle  : java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList

02-15 16:12:24.321  5361  5361 W Bundle  : 	at android.os.Bundle.getParcelableArrayList(Bundle.java:838)

02-15 16:12:24.321  5361  5361 W Bundle  : 	at android.content.Intent.getParcelableArrayListExtra(Intent.java:6239)

02-15 16:12:24.321  5361  5361 W Bundle  : 	at X.8ZM.b(:1186007)

02-15 16:12:24.321  5361  5361 W Bundle  : 	at X.8ak.a(:1187601)

02-15 16:12:24.321  5361  5361 W Bundle  : 	at com.facebook.messaging.sharing.ShareLauncherActivity.c(:249031)

02-15 16:12:24.321  5361  5361 W Bundle  : 	at com.facebook.base.activity.FbFragmentActivity.onCreate(:113491)

I found a solution though its a terrible hack.

Go into this file:

Change line:

std::string fullpath = FileUtils::getInstance()->getWritablePath() + fileName;

to

std::string fullpath = ‘/sdcard/’ + fileName;

This will work but it is bad because its a hard coded path. If you find a more safer method let me know, this will work for now though.

I’ve come up with a full solution to this:

change the code in ccrendertexture.cpp:

#include “platform/android/jni/JniHelper.h”
#include <jni.h>

bool RenderTexture::saveToFile(const std::string& fileName, Image::Format format, bool isRGBA, std::function<void (RenderTexture*, const std::string&)> callback)
{
    CCASSERT(format == Image::Format::JPG || format == Image::Format::PNG,
             "the image can only be saved as JPG or PNG format");
    if (isRGBA && format == Image::Format::JPG) CCLOG("RGBA is not supported for JPG format");

    _saveFileCallback = callback;

    jstring dir;
    std::string str;
    cocos2d::JniMethodInfo methodInfo;
    if (cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/javascript/AppActivity", "GetExternalStorageLocation", "()Ljava/lang/String;")) {
        dir = (jstring) methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
        str = JniHelper::jstring2string(dir);
    }

    std::string fullpath = str + "/" + fileName;
    _saveToFileCommand.init(_globalZOrder);
    _saveToFileCommand.func = CC_CALLBACK_0(RenderTexture::onSaveToFile, this, fullpath, isRGBA);

    Director::getInstance()->getRenderer()->addCommand(&_saveToFileCommand);
    return true;
}

then in your AppActivity.java add:

import android.os.Environment;

public static String GetExternalStorageLocation()
    {
        String tag = "JniTest";
        return Environment.getExternalStorageDirectory().getPath();
    }

back in your js your saved function should have the path in the parameters(path is what you want to use):

var saved = function (rt, path, rgb) {

I have tried it, but I’m using cocos creator and I have no idea how to change the cpp part… I have made your changement, then I had jstring undefined because jni.h wasn’t found. So I have added the $(JAVA_HOME)\include\win32 and $(JAVA_HOME)\include in the additional include directories. Now I have a unresolved external symbol because jnihelper.cpp isn’t in the project… I don’t know how to resolve it.

And even if I can compile the cpp, what I am suppose to do with the results ? replace the .o I can found in the build folder of my cocos creator project ?

And I’m not sure if it’s a path problem, because with my solution, the game is suppose to write the screenshot in a public folder (/storage/emulated/0/). But nothing is written in android…

you are probably missing this header file in cpp:

#include < jni.h>

       ^ remove the space

As for the result… you get the result in the ‘saved’ function so you use that. It is the second argument that you use as a string for the path of the img.

var saved = function (rt, path, rgb) {
var shareInfo = {};
shareInfo.text = “This new game is HOT!”;
shareInfo.title = “Hottiest”;
shareInfo.image = path;
shareInfo.link = “http://abyssal-studio.com”;
sdkbox.PluginShare.nativeShare(shareInfo);
};

Against which Android SDK u compiling? If it is Android-23 and u running app on Android 6.x+ then u should ask for permission runtime - with manifest not enough

How do I check which android version i’m compiling with? I’ve tested on my 6.0.1 device but I do not have any issues with permissions.

Target sdk should be in Android manifest or build.gradle file I’m not sure where they located under Cocos-creator - probably same structure as default cocos proj.android-studio/app check there build.gradle targetsdkversion

I finally did it!!
First I have changed the function Init in cocos2dxHelper.java by replacing this line:
Cocos2dxHelper.sFileDirectory = activity.getFilesDir().getAbsolutePath();
by this one:
Cocos2dxHelper.sFileDirectory = Environment.getExternalStorageDirectory().getPath();

then in the same file, in the function getCocos2dxWritablePath, I have asked the runtime permission to write file using this:

if (ContextCompat.checkSelfPermission(Cocos2dxHelper.sActivity,
                android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(Cocos2dxHelper.sActivity,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            } else {

                ActivityCompat.requestPermissions(Cocos2dxHelper.sActivity,
                        new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        1);
            }
        }
        return Cocos2dxHelper.sFileDirectory;

(don’t forget to use)

import android.support.v4.content.ContextCompat;
import android.support.v4.app.ActivityCompat;

Now it works !

2 Likes

Thanks for including your solution as well, much simpler :slight_smile: