Android APK Expansoins

Anyone have any experience dealing with APK Expansion files on Android? I am doing this on top of adding support for loading multiple texture compression formats depending on the current device.
I hope to create different APKs with different APK Expansions that include a specific compression format.

Before I jump into the implementation, I was wondering if anyone has done this successfully yet.

Skipping most of the initial details, I am stuck on one point: reading from the expansion file.
The documentation suggests ( for good reason ) that the expansion file stay a single file after being downloaded to the device and we use an Android API to stream content from the file ( they provide code to stream files a ZIP file ).

I see a method CCFileUtils::getFileDataFromZip that takes in a path to a zip file and a path to a file.

Will this method open up files inside the ZIP file, relative to the ZIP? For example:

main.11111.com.cboxgames.Idonia.zip
image.pvr.ccz

Open textures\image.pvr.ccz without extracting the ZIP file and storing it elsewhere.
getFileDataFromZip looks like it is doing this but I’m not 100% sure.

Thanks!

Hello,

Any luck with this? I’m working on the same issue.

Yeap, you can use CCFileUtils::getFileDataFromZip() to read file data which is packed into a zip file.
Cocos2d-x also use it to loading resources.

is there a way to modify the cocos2d-x source code to force it read all resources from APK expansion files?
I guess can I change

void CCFileUtils::setResourcePath(const char* pszResourcePath){
CCAssert(pszResourcePath != NULL, “[FileUtils setRelativePath] — wrong relative path”);
string tmp(pszResourcePath);
if ((! pszResourcePath) || tmp.find(“.apk”) string::npos){
return;
}
s_strResourcePath = pszResourcePath;
}

modify this line

    if ((! pszResourcePath) || tmp.find(".apk")  string::npos)

to

if (! pszResourcePath)

and then call setResourcePath(“sdcard/obb/blablabla.file”)

and for All of my assets, I put them into an “assets” folder and zip them into a apk expansion. upload together with my apk file, will it work?

Hello Michael and Matt.

I have successfully gotten this to work.

Please refer to this documentation for more information (especially the naming scheme and downloader library, very helpful).
http://developer.android.com/guide/market/expansion-files.html

For your first modification, as pointed out by the Google documentation, you need to look for an “.obb”. This is what Google renames all expansion extensions to.

For the getFileData method, you should also modify the second if-statement to something like:

// As noted in the documentation, this is where expansion files are saved to: 
// /Android/obb//[main|patch]...obb
if ( s_strResourcePath.find( "Android/obb/" ) != std::string::npos )
    {
        // Read from expansion
        pData =  CCFileUtils::getFileDataFromZip(s_strResourcePath.c_str(), fullPath.c_str(), pSize);
    }
    else if (pszFileName[0] != '/')
    {
        // read from apk
        fullPath.insert(0, "assets/");
        pData =  CCFileUtils::getFileDataFromZip(s_strResourcePath.c_str(), fullPath.c_str(), pSize);
    }

You will also have to make modifications to the Java code to read from the expansion files as well:
Cocos2dxMusic.java, Cocos2dxSound.java and possibly Cocos2dxTypefaces.java. I personally didn’t modify Cocos2dxTypefaces.java
and just put all fonts inside the assets folder.

Hope that helps!

Sorry but can you provide a bit more in detail what changes you made to make the magic happen, especially on the java side ?
My apk expansion is loader well but the app is crashing when loading assets.
Thanks in advance

Hey, sure.

It will help me if you could provide where exactly it is crashing. A stack trace would be ideal.

Thanks, I finally got it worked 5 minutes ago by settings CCFileUtils::setResourcePath(“/sdcard/Android/obb/(path-to-my-obb-file)”); in AppDelegate. I was just missing /sdcard
Now my images are loading fine, but I don’t know what to do on the java side for music and sounds.

As an example, in Cocos2dxSound.java, here are the modifications that need to be made.

createSoundIdFromAsset needs to be modified to read from a zip file. We can use the library provided by Google to help us with this ( included with the downloader library I believe ).

public int createSoundIdFromAsset(String path){
        int soundId = INVALID_SOUND_ID;

        try {
            if (path.startsWith("/")){
                soundId = mSoundPool.load(path, 0);
            }
            else {
                AssetFileDescriptor assetFileDescritor = zip_resource_file_.getAssetFileDescriptor( path );
                soundId = mSoundPool.load( assetFileDescritor, 0 );
            }           
        } catch(Exception e){
            soundId = INVALID_SOUND_ID;
             Log.e(TAG, "error: " + e.getMessage(), e);
        }

        return soundId;
    }

zip_resource_file_ is created by loading the zip file in the constructor:

SharedPreferences pref = context.getSharedPreferences( IdoniaDownloaderActivity.PREFS_NAME, 0 );
        try {
            zip_resource_file_ = APKExpansionSupport.getAPKExpansionZipFile( 
                    context, 
                    pref.getInt( 
                        IdoniaDownloaderActivity.KEY_EXPANSION_FILE_VERSION, 
                        IdoniaDownloaderActivity.DEBUG_APP_VERSION ),
                        0 );
        } catch ( IOException e ) {
            Log.e( "Cocos2dxMusic" ,  "Initializing zip_resource_file_", e );
        }

I used SharedPreferences to help store and retrieve the location/name/version number of the expansion files.

I want to comment about the APK expansion packs though. After going through the hassle of uploading different expansion packs and application apk combinations for all the texture types, I don’t think I will continue to use this method in the future (depending on the game and how often it will be updated).

The reason for this is that when uploading to the developer console, the application version numbers are a bit confusing to deal with and if you make a mistake, you have to wait a LONG time to upload your application/expansion pack combo. Also, you have to organize the APKs so that the AndroidManifest.xml file properly define what GL extensions each apk/expansion pack support. Way too many ways to make a mistake.

Not to mention, the uploader on the developer console isn’t that great either because it provides minimal feed back during the upload process.

So depending on the size of the application and number of texture formats you support, it might be alright.
We decided not to for our game (~87 MB each with 4 texture types) and put the zip files on S3 and download them ourselves based on what the device supported. This allowed us to have a single application APK ( implieng also a single AndroidManifest.xml file ) that has the benefit of making the application update and versioning process much simpler.

The modifications mentioned above though stayed relevant, except for the location of the ZIP file we download ourselves.

Working perfectly, excellent. Thanks a lot Irwin.

Would either of you guys who have it working be so kind to post what to do inside your main java file to download the expansion if needed etc. I can’t figure out how to get that working cleanly. The sample files from the Android page has so much stuff in it that seems unnecessary. Trying to find the barebones implementation.

Hello,

Has anyone tried this with Cocos2D-X 3.x ?

Thank you