Merged changes from Alexey Petruchik to support Android obb files
authorSam Lantinga <slouken@libsdl.org>
Sat, 21 Jun 2014 20:35:36 -0700
changeset 8900537445b25e4e
parent 8895 070b7f655147
parent 8899 7f19291e720c
child 8902 5192470456ae
Merged changes from Alexey Petruchik to support Android obb files
http://developer.android.com/google/play/expansion-files.html
android-project/src/org/libsdl/app/SDLActivity.java
include/SDL_hints.h
src/core/android/SDL_android.c
     1.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java	Sat Jun 21 17:31:36 2014 -0700
     1.2 +++ b/android-project/src/org/libsdl/app/SDLActivity.java	Sat Jun 21 20:35:36 2014 -0700
     1.3 @@ -1,10 +1,13 @@
     1.4  package org.libsdl.app;
     1.5  
     1.6 +import java.io.IOException;
     1.7 +import java.io.InputStream;
     1.8  import java.util.ArrayList;
     1.9  import java.util.Arrays;
    1.10  import java.util.Collections;
    1.11  import java.util.Comparator;
    1.12  import java.util.List;
    1.13 +import java.lang.reflect.Method;
    1.14  
    1.15  import android.app.*;
    1.16  import android.content.*;
    1.17 @@ -20,7 +23,6 @@
    1.18  import android.media.*;
    1.19  import android.hardware.*;
    1.20  
    1.21 -
    1.22  /**
    1.23      SDL Activity
    1.24  */
    1.25 @@ -296,6 +298,7 @@
    1.26                                                 int is_accelerometer, int nbuttons, 
    1.27                                                 int naxes, int nhats, int nballs);
    1.28      public static native int nativeRemoveJoystick(int device_id);
    1.29 +    public static native String nativeGetHint(String name);
    1.30  
    1.31      /**
    1.32       * This method is called by SDL using JNI.
    1.33 @@ -531,7 +534,52 @@
    1.34              mJoystickHandler.pollInputDevices();
    1.35          }
    1.36      }
    1.37 -    
    1.38 +
    1.39 +    // APK extension files support
    1.40 +
    1.41 +    /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
    1.42 +    private Object expansionFile;
    1.43 +
    1.44 +    /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
    1.45 +    private Method expansionFileMethod;
    1.46 +
    1.47 +    public InputStream openAPKExtensionInputStream(String fileName) throws IOException {
    1.48 +        // Get a ZipResourceFile representing a merger of both the main and patch files
    1.49 +        if (expansionFile == null) {
    1.50 +            Integer mainVersion = Integer.parseInt(nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"));
    1.51 +            Integer patchVersion = Integer.parseInt(nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"));
    1.52 +
    1.53 +            try {
    1.54 +                // To avoid direct dependency on Google APK extension library that is
    1.55 +                // not a part of Android SDK we access it using reflection
    1.56 +                expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
    1.57 +                    .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
    1.58 +                    .invoke(null, this, mainVersion, patchVersion);
    1.59 +
    1.60 +                expansionFileMethod = expansionFile.getClass()
    1.61 +                    .getMethod("getInputStream", String.class);
    1.62 +            } catch (Exception ex) {
    1.63 +                ex.printStackTrace();
    1.64 +                expansionFile = null;
    1.65 +                expansionFileMethod = null;
    1.66 +            }
    1.67 +        }
    1.68 +
    1.69 +        // Get an input stream for a known file inside the expansion file ZIPs
    1.70 +        InputStream fileStream;
    1.71 +        try {
    1.72 +            fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
    1.73 +        } catch (Exception ex) {
    1.74 +            ex.printStackTrace();
    1.75 +            fileStream = null;
    1.76 +        }
    1.77 +
    1.78 +        if (fileStream == null) {
    1.79 +            throw new IOException();
    1.80 +        }
    1.81 +
    1.82 +        return fileStream;
    1.83 +    }
    1.84  }
    1.85  
    1.86  /**
     2.1 --- a/include/SDL_hints.h	Sat Jun 21 17:31:36 2014 -0700
     2.2 +++ b/include/SDL_hints.h	Sat Jun 21 20:35:36 2014 -0700
     2.3 @@ -457,6 +457,16 @@
     2.4   */
     2.5  #define SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES    "SDL_VIDEO_MAC_FULLSCREEN_SPACES"
     2.6  
     2.7 +/**
     2.8 + * \brief Android APK expansion main file version. Should be a string number like "1", "2" etc.
     2.9 + */
    2.10 +#define SDL_HINT_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION "SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"
    2.11 + 
    2.12 +/**
    2.13 + * \brief Android APK expansion patch file version. Should be a string number like "1", "2" etc.
    2.14 + */
    2.15 +#define SDL_HINT_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION "SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"
    2.16 +
    2.17  
    2.18  /**
    2.19   *  \brief  An enumeration of hint priorities
     3.1 --- a/src/core/android/SDL_android.c	Sat Jun 21 17:31:36 2014 -0700
     3.2 +++ b/src/core/android/SDL_android.c	Sat Jun 21 20:35:36 2014 -0700
     3.3 @@ -385,7 +385,15 @@
     3.4      (*env)->ReleaseStringUTFChars(env, text, utftext);
     3.5  }
     3.6  
     3.7 +jstring Java_org_libsdl_app_SDLActivity_nativeGetHint(JNIEnv* env, jclass cls, jstring name) {
     3.8 +    const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
     3.9 +    const char *hint = SDL_GetHint(utfname);
    3.10  
    3.11 +    jstring result = (*env)->NewStringUTF(env, hint);
    3.12 +    (*env)->ReleaseStringUTFChars(env, name, utfname);
    3.13 +
    3.14 +    return result;
    3.15 +}
    3.16  
    3.17  /*******************************************************************************
    3.18               Functions called by SDL into Java
    3.19 @@ -758,7 +766,14 @@
    3.20                  "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
    3.21          inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
    3.22          if (Android_JNI_ExceptionOccurred(false)) {
    3.23 -            goto failure;
    3.24 +            // Try fallback to APK Extension files
    3.25 +            mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
    3.26 +                "openAPKExtensionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
    3.27 +            inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
    3.28 +
    3.29 +            if (Android_JNI_ExceptionOccurred(false)) {
    3.30 +                goto failure;
    3.31 +            }
    3.32          }
    3.33  
    3.34          ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);