android: fix config_GetUserDir for generic dirs
authorThomas Guillem <thomas@gllm.fr>
Wed, 13 Jul 2016 13:19:14 +0000 (15:19 +0200)
committerThomas Guillem <thomas@gllm.fr>
Wed, 13 Jul 2016 13:43:39 +0000 (15:43 +0200)
Call android.os.Environment.getExternalStoragePublicDirectory() from JNI to get
the path to a valid directory.

src/android/specific.c

index 959dc37..1337157 100644 (file)
 #include <jni.h>
 
 static JavaVM *s_jvm = NULL;
+#define GENERIC_DIR_COUNT (VLC_VIDEOS_DIR - VLC_DESKTOP_DIR + 1)
+static char *ppsz_generic_names[GENERIC_DIR_COUNT] = {};
+static struct {
+    struct {
+        jclass clazz;
+        jmethodID getExternalStoragePublicDirectory;
+    } Environment;
+    struct {
+        jmethodID getAbsolutePath;
+    } File;
+} fields = { .Environment.clazz = NULL };
+
+static char *
+get_java_string(JNIEnv *env, jclass clazz, const char *psz_name)
+{
+    jfieldID id = (*env)->GetStaticFieldID(env, clazz, psz_name,
+                                           "Ljava/lang/String;");
+    if ((*env)->ExceptionCheck(env))
+        return NULL;
+
+    jstring jstr = (*env)->GetStaticObjectField(env, clazz, id);
+
+    const char *psz_str = (*env)->GetStringUTFChars(env, jstr, 0);
+    if (psz_str == NULL)
+        return NULL;
+
+    char *psz_strdup = strdup(psz_str);
+
+    (*env)->ReleaseStringUTFChars(env, jstr, psz_str);
+    (*env)->DeleteLocalRef(env, jstr);
+
+    return psz_strdup;
+}
+
+void
+JNI_OnUnload(JavaVM* vm, void* reserved)
+{
+    (void) reserved;
+
+    for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
+        free(ppsz_generic_names[i]);
+
+    if (fields.Environment.clazz)
+    {
+        JNIEnv* env = NULL;
+        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
+            return;
+        (*env)->DeleteGlobalRef(env, fields.Environment.clazz);
+    }
+}
 
 /* This function is called when the libvlcore dynamic library is loaded via the
  * java.lang.System.loadLibrary method. Therefore, s_jvm will be already set
@@ -40,16 +90,61 @@ static JavaVM *s_jvm = NULL;
 jint
 JNI_OnLoad(JavaVM *vm, void *reserved)
 {
-    (void) reserved;
     s_jvm = vm;
+    JNIEnv* env = NULL;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
+        return -1;
+
+    jclass clazz = (*env)->FindClass(env, "android/os/Environment");
+    if ((*env)->ExceptionCheck(env))
+        return -1;
+
+    static const char *ppsz_env_names[GENERIC_DIR_COUNT] = {
+        NULL,                   /* VLC_DESKTOP_DIR */
+        "DIRECTORY_DOWNLOADS",  /* VLC_DOWNLOAD_DIR */
+        NULL,                   /* VLC_TEMPLATES_DIR */
+        NULL,                   /* VLC_PUBLICSHARE_DIR */
+        "DIRECTORY_DOCUMENTS",  /* VLC_DOCUMENTS_DIR */
+        "DIRECTORY_MUSIC",      /* VLC_MUSIC_DIR */
+        "DIRECTORY_PICTURES",   /* VLC_PICTURES_DIR */
+        "DIRECTORY_MOVIES",     /* VLC_VIDEOS_DIR */
+    };
+    for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
+    {
+        if (ppsz_env_names[i] != NULL)
+        {
+            ppsz_generic_names[i] = get_java_string(env, clazz,
+                                                   ppsz_env_names[i]);
+            if (!ppsz_generic_names[i])
+                goto error;
+        }
+    }
+
+    fields.Environment.clazz = (*env)->NewGlobalRef(env, clazz);
+    fields.Environment.getExternalStoragePublicDirectory =
+        (*env)->GetStaticMethodID(env, clazz,
+                                  "getExternalStoragePublicDirectory",
+                                  "(Ljava/lang/String;)Ljava/io/File;");
+    if ((*env)->ExceptionCheck(env))
+        goto error;
+    (*env)->DeleteLocalRef(env, clazz);
+
+    clazz = (*env)->FindClass(env, "java/io/File");
+    fields.File.getAbsolutePath =
+        (*env)->GetMethodID(env, clazz, "getAbsolutePath",
+                           "()Ljava/lang/String;");
+    if ((*env)->ExceptionCheck(env))
+        goto error;
+    (*env)->DeleteLocalRef(env, clazz);
+
     return JNI_VERSION_1_2;
-}
 
-void
-JNI_OnUnload(JavaVM* vm, void* reserved)
-{
-    (void) vm;
-    (void) reserved;
+error:
+    if (clazz)
+        (*env)->DeleteLocalRef(env, clazz);
+    JNI_OnUnload(vm, reserved);
+    return -1;
 }
 
 void
@@ -89,6 +184,62 @@ fallback:
     return psz_default_dir != NULL ? strdup(psz_default_dir) : NULL;
 }
 
+static char *config_GetGenericDir(const char *psz_name)
+{
+    JNIEnv *env;
+    bool b_detach;
+    char *psz_ret = NULL;
+
+    if ((*s_jvm)->GetEnv(s_jvm, (void **)&env, JNI_VERSION_1_2) != JNI_OK)
+    {
+        /* attach the thread to the Java VM */
+        JavaVMAttachArgs args;
+
+        args.version = JNI_VERSION_1_2;
+        args.name = "config_GetGenericDir";
+        args.group = NULL;
+
+        if ((*s_jvm)->AttachCurrentThread(s_jvm, &env, &args) != JNI_OK)
+            return NULL;
+        b_detach = true;
+    }
+    else
+        b_detach = false;
+
+    jstring jname= (*env)->NewStringUTF(env, psz_name);
+    if ((*env)->ExceptionCheck(env))
+    {
+        (*env)->ExceptionClear(env);
+        jname = NULL;
+    }
+    if (jname == NULL)
+        goto error;
+
+    jobject jfile = (*env)->CallStaticObjectMethod(env,
+                        fields.Environment.clazz,
+                        fields.Environment.getExternalStoragePublicDirectory,
+                        jname);
+    (*env)->DeleteLocalRef(env, jname);
+    if (jfile == NULL)
+        goto error;
+
+    jstring jpath = (*env)->CallObjectMethod(env, jfile,
+                                             fields.File.getAbsolutePath);
+    (*env)->DeleteLocalRef(env, jfile);
+
+    const char *psz_path = (*env)->GetStringUTFChars(env, jpath, 0);
+    if (psz_path == NULL)
+        goto error;
+    psz_ret = strdup(psz_path);
+    (*env)->ReleaseStringUTFChars(env, jpath, psz_path);
+    (*env)->DeleteLocalRef(env, jpath);
+
+error:
+    if (b_detach)
+        (*s_jvm)->DetachCurrentThread(s_jvm);
+    return psz_ret;
+}
+
 char *config_GetUserDir (vlc_userdir_t type)
 {
     switch (type)
@@ -112,7 +263,12 @@ char *config_GetUserDir (vlc_userdir_t type)
         case VLC_MUSIC_DIR:
         case VLC_PICTURES_DIR:
         case VLC_VIDEOS_DIR:
-            return NULL;
+        {
+            assert(type >= VLC_DESKTOP_DIR && type <= VLC_VIDEOS_DIR);
+            const char *psz_name = ppsz_generic_names[type - VLC_DESKTOP_DIR];
+            if (psz_name != NULL)
+                return config_GetGenericDir(psz_name);
+        }
     }
     return NULL;
 }