How to access "Extra Android Assets" within Android app?


#1

I have a folder of audio files that I am including in my app via the “Extra Android Assets” Projucer Option.

How do you actually get these assets in the app? I’m fairly new to Android & didn’t see any other documentation about this.


#2

Solved with this (run once upon app initialization):

// Copies the .wav files in /assets into the app data directory upon first launch.
// Subsequent launches will only copy over new assets, if there are any.
#if JUCE_ANDROID
    // Directory to store /assets files
    const auto dataDirectory = File::getSpecialLocation (File::userApplicationDataDirectory).getChildFile ("data");    
    if (! dataDirectory.exists()) {
        dataDirectory.createDirectory();
    }
    // See if there are any files installed from previous launches of the app
    Array<File> installedData = dataDirectory.findChildFiles (File::findFiles, true, "*");
    StringArray installedDataFilenames;
    for (auto data : installedData) {
        installedDataFilenames.add (data.getFileName());
    }
    // Extract entries from the APK file under the assets/ folder (generated by ProJucer)
    ZipFile zip (File::getSpecialLocation (File::currentApplicationFile));
    for (int i = 0; i < zip.getNumEntries(); i++) {
        if (auto* entry = zip.getEntry (i)) {
            // Only worried about .wav files for now - don't want to copy the entire APK
            if (entry->filename.endsWithIgnoreCase (".wav")) {
                // Will only copy resource over once
                if (! installedDataFilenames.contains (entry->filename.fromFirstOccurrenceOf ("assets/", false, false))) {
                    // File not installed yet, unpack it.
                    zip.uncompressEntry (i, dataDirectory, true);
                }
            }
        }
    }
#endif

I need to be able to treat these assets as traditional “files”, so merely uncompressing them and using the raw data was not an option.


#3

Using the above example, I access the actual files by this:

File assetsDirectory = File::getSpecialLocation (File::userApplicationDataDirectory).getChildFile ("data").getChildFile ("assets");
Array<File> assets = assetsDirectory.findChildFiles (File::findFiles, false, "*");

#4

Have you looked at how we do this in the DemoRunner on Android? Take a look at the createAssetInputStream() method in JUCE/examples/Assets/DemoUtilities.h.


#5

Yes, but in my case I am using an old C library that has no use for input streams or raw data — it actually needs a traditional File reference (don’t want to touch the old, ugly code if I can avoid it).

So either way I’d need to expand the .zip assets somewhere on the file system.


#6

How you work with the assets folder depends on what you want to do.

If your app has assets that need to be decompressed before they can be used (for example compressed audio files or gpu textures), you only want to do this once. In this case @cpenny’s approach is best: extract the files and copy them to internal storage on app first run. Then access those files directly.

If you just need to access the files in the assets folder you can use AssetManager which presumably JUCE’s createAssetInputStream calls, in order to access the asset directly.

More info about Android’s filesystem structure: https://developer.android.com/guide/topics/data/data-storage


#7

That’s great, thanks @Don_Turner for verifying this. I was unsure, if I missed something, but reading a bit more about storage and apk it makes sense now.
I was also a bit confused, since on the simulator sometimes I didn’t seem to have the full apk, but just the JNI and some MANI* files. But that could have happened on my end.