qml: restore the focus on the last album when navigating back to the album view
[vlc.git] / src / android / specific.c
blob1b74f522a3264642cd7233e72357e575f9564ba4
1 /*****************************************************************************
2 * specific.c: stubs for Android OS-specific initialization
3 *****************************************************************************
4 * Copyright © 2016 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <assert.h>
26 #include <errno.h>
28 #include <vlc_common.h>
29 #include <vlc_network.h>
30 #include <vlc_fs.h>
31 #include "../libvlc.h"
32 #include "config/configuration.h"
34 #include <string.h>
35 #include <jni.h>
37 static JavaVM *s_jvm = NULL;
38 #define GENERIC_DIR_COUNT (VLC_VIDEOS_DIR - VLC_DESKTOP_DIR + 1)
39 static char *ppsz_generic_names[GENERIC_DIR_COUNT] = {};
40 static struct {
41 struct {
42 jclass clazz;
43 jmethodID getExternalStoragePublicDirectory;
44 } Environment;
45 struct {
46 jmethodID getAbsolutePath;
47 } File;
48 struct {
49 jclass clazz;
50 jmethodID getProperty;
51 } System;
52 } fields = { .Environment.clazz = NULL };
54 static char *
55 get_java_string(JNIEnv *env, jclass clazz, const char *psz_name)
57 jfieldID id = (*env)->GetStaticFieldID(env, clazz, psz_name,
58 "Ljava/lang/String;");
59 if ((*env)->ExceptionCheck(env))
61 (*env)->ExceptionClear(env);
62 return NULL;
65 jstring jstr = (*env)->GetStaticObjectField(env, clazz, id);
67 const char *psz_str = (*env)->GetStringUTFChars(env, jstr, 0);
68 if (psz_str == NULL)
69 return NULL;
71 char *psz_strdup = strdup(psz_str);
73 (*env)->ReleaseStringUTFChars(env, jstr, psz_str);
74 (*env)->DeleteLocalRef(env, jstr);
76 return psz_strdup;
79 void
80 JNI_OnUnload(JavaVM* vm, void* reserved)
82 (void) reserved;
84 for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
85 free(ppsz_generic_names[i]);
87 JNIEnv* env = NULL;
88 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
89 return;
91 if (fields.Environment.clazz)
92 (*env)->DeleteGlobalRef(env, fields.Environment.clazz);
94 if (fields.System.clazz)
95 (*env)->DeleteGlobalRef(env, fields.System.clazz);
98 /* This function is called when the libvlcore dynamic library is loaded via the
99 * java.lang.System.loadLibrary method. Therefore, s_jvm will be already set
100 * when libvlc_InternalInit is called. */
101 jint
102 JNI_OnLoad(JavaVM *vm, void *reserved)
104 s_jvm = vm;
105 JNIEnv* env = NULL;
107 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
108 return -1;
110 jclass clazz = (*env)->FindClass(env, "android/os/Environment");
111 if ((*env)->ExceptionCheck(env))
112 return -1;
114 static const char *ppsz_env_names[GENERIC_DIR_COUNT] = {
115 NULL, /* VLC_DESKTOP_DIR */
116 "DIRECTORY_DOWNLOADS", /* VLC_DOWNLOAD_DIR */
117 NULL, /* VLC_TEMPLATES_DIR */
118 NULL, /* VLC_PUBLICSHARE_DIR */
119 "DIRECTORY_DOCUMENTS", /* VLC_DOCUMENTS_DIR */
120 "DIRECTORY_MUSIC", /* VLC_MUSIC_DIR */
121 "DIRECTORY_PICTURES", /* VLC_PICTURES_DIR */
122 "DIRECTORY_MOVIES", /* VLC_VIDEOS_DIR */
124 for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
126 if (ppsz_env_names[i] != NULL)
127 ppsz_generic_names[i] = get_java_string(env, clazz,
128 ppsz_env_names[i]);
131 fields.Environment.clazz = (*env)->NewGlobalRef(env, clazz);
132 fields.Environment.getExternalStoragePublicDirectory =
133 (*env)->GetStaticMethodID(env, clazz,
134 "getExternalStoragePublicDirectory",
135 "(Ljava/lang/String;)Ljava/io/File;");
136 if ((*env)->ExceptionCheck(env))
137 goto error;
138 (*env)->DeleteLocalRef(env, clazz);
140 clazz = (*env)->FindClass(env, "java/io/File");
141 fields.File.getAbsolutePath =
142 (*env)->GetMethodID(env, clazz, "getAbsolutePath",
143 "()Ljava/lang/String;");
144 if ((*env)->ExceptionCheck(env))
145 goto error;
146 (*env)->DeleteLocalRef(env, clazz);
148 clazz = (*env)->FindClass(env, "java/lang/System");
149 if ((*env)->ExceptionCheck(env))
150 goto error;
151 fields.System.clazz = (*env)->NewGlobalRef(env, clazz);
152 fields.System.getProperty =
153 (*env)->GetStaticMethodID(env, clazz, "getProperty",
154 "(Ljava/lang/String;)Ljava/lang/String;");
155 (*env)->DeleteLocalRef(env, clazz);
157 return JNI_VERSION_1_2;
159 error:
160 if (clazz)
161 (*env)->DeleteLocalRef(env, clazz);
162 JNI_OnUnload(vm, reserved);
163 return -1;
166 void
167 system_Init(void)
171 void
172 system_Configure(libvlc_int_t *p_libvlc, int i_argc, const char *const pp_argv[])
174 (void)i_argc; (void)pp_argv;
175 assert(s_jvm != NULL);
176 var_Create(p_libvlc, "android-jvm", VLC_VAR_ADDRESS);
177 var_SetAddress(p_libvlc, "android-jvm", s_jvm);
180 static char *config_GetHomeDir(const char *psz_dir, const char *psz_default_dir)
182 char *psz_home = getenv("HOME");
183 if (psz_home == NULL)
184 goto fallback;
186 if (psz_dir == NULL)
187 return strdup(psz_home);
189 char *psz_fullpath;
190 if (asprintf(&psz_fullpath, "%s/%s", psz_home, psz_dir) == -1)
191 goto fallback;
192 if (vlc_mkdir(psz_fullpath, 0700) == -1 && errno != EEXIST)
194 free(psz_fullpath);
195 goto fallback;
197 return psz_fullpath;
199 fallback:
200 return psz_default_dir != NULL ? strdup(psz_default_dir) : NULL;
203 static JNIEnv *get_env(bool *p_detach)
205 JNIEnv *env;
206 if ((*s_jvm)->GetEnv(s_jvm, (void **)&env, JNI_VERSION_1_2) != JNI_OK)
208 /* attach the thread to the Java VM */
209 JavaVMAttachArgs args;
211 args.version = JNI_VERSION_1_2;
212 args.name = "config_GetGenericDir";
213 args.group = NULL;
215 if ((*s_jvm)->AttachCurrentThread(s_jvm, &env, &args) != JNI_OK)
216 return NULL;
217 *p_detach = true;
219 else
220 *p_detach = false;
221 return env;
224 static void release_env(bool b_detach)
226 if (b_detach)
227 (*s_jvm)->DetachCurrentThread(s_jvm);
230 static char *config_GetGenericDir(const char *psz_name)
232 JNIEnv *env;
233 bool b_detach;
234 char *psz_ret = NULL;
236 env = get_env(&b_detach);
237 if (env == NULL)
238 return NULL;
240 jstring jname= (*env)->NewStringUTF(env, psz_name);
241 if ((*env)->ExceptionCheck(env))
243 (*env)->ExceptionClear(env);
244 jname = NULL;
246 if (jname == NULL)
247 goto error;
249 jobject jfile = (*env)->CallStaticObjectMethod(env,
250 fields.Environment.clazz,
251 fields.Environment.getExternalStoragePublicDirectory,
252 jname);
253 (*env)->DeleteLocalRef(env, jname);
254 if (jfile == NULL)
255 goto error;
257 jstring jpath = (*env)->CallObjectMethod(env, jfile,
258 fields.File.getAbsolutePath);
259 (*env)->DeleteLocalRef(env, jfile);
261 const char *psz_path = (*env)->GetStringUTFChars(env, jpath, 0);
262 if (psz_path == NULL)
263 goto error;
264 psz_ret = strdup(psz_path);
265 (*env)->ReleaseStringUTFChars(env, jpath, psz_path);
266 (*env)->DeleteLocalRef(env, jpath);
268 error:
269 release_env(b_detach);
270 return psz_ret;
273 char *config_GetUserDir (vlc_userdir_t type)
275 switch (type)
277 case VLC_USERDATA_DIR:
278 return config_GetHomeDir(".share",
279 "/sdcard/Android/data/org.videolan.vlc");
280 case VLC_CACHE_DIR:
281 return config_GetHomeDir(".cache",
282 "/sdcard/Android/data/org.videolan.vlc/cache");
283 case VLC_HOME_DIR:
284 return config_GetHomeDir(NULL, NULL);
285 case VLC_CONFIG_DIR:
286 return config_GetHomeDir(".config", NULL);
288 case VLC_DESKTOP_DIR:
289 case VLC_DOWNLOAD_DIR:
290 case VLC_TEMPLATES_DIR:
291 case VLC_PUBLICSHARE_DIR:
292 case VLC_DOCUMENTS_DIR:
293 case VLC_MUSIC_DIR:
294 case VLC_PICTURES_DIR:
295 case VLC_VIDEOS_DIR:
297 assert(type >= VLC_DESKTOP_DIR && type <= VLC_VIDEOS_DIR);
298 const char *psz_name = ppsz_generic_names[type - VLC_DESKTOP_DIR];
299 if (psz_name != NULL)
300 return config_GetGenericDir(psz_name);
303 return NULL;
306 char *config_GetSysPath(vlc_sysdir_t type, const char *filename)
308 char *dir = NULL;
310 switch (type)
312 case VLC_SYSDATA_DIR:
313 dir = strdup("/system/usr/share");
314 break;
315 case VLC_LIB_DIR:
316 dir = config_GetLibDir();
317 break;
318 default:
319 break;
322 if (filename == NULL || dir == NULL)
323 return dir;
325 char *path;
326 if (unlikely(asprintf(&path, "%s/%s", dir, filename) == -1))
327 path = NULL;
328 free(dir);
329 return path;
333 * Determines the network proxy server to use (if any).
335 * This function fetch the Android proxy using the System.getProperty() method
336 * with "http.proxyHost" and "http.proxyPort" keys. This is working only for
337 * Android 4.0 and after.
339 * @param url absolute URL for which to get the proxy server (unused)
340 * @return proxy URL, NULL if no proxy or error
342 char *vlc_getProxyUrl(const char *url)
344 VLC_UNUSED(url);
345 JNIEnv *env;
346 bool b_detach;
347 char *psz_ret = NULL;
348 const char *psz_host = NULL, *psz_port = NULL;
349 jstring jhost = NULL, jport = NULL;
351 env = get_env(&b_detach);
352 if (env == NULL)
353 return NULL;
355 /* Fetch "http.proxyHost" property */
356 jstring jkey = (*env)->NewStringUTF(env, "http.proxyHost");
357 if ((*env)->ExceptionCheck(env))
359 (*env)->ExceptionClear(env);
360 jkey = NULL;
362 if (jkey == NULL)
363 goto end;
365 jhost = (*env)->CallStaticObjectMethod(env, fields.System.clazz,
366 fields.System.getProperty, jkey);
367 (*env)->DeleteLocalRef(env, jkey);
368 if (jhost == NULL)
369 goto end;
371 psz_host = (*env)->GetStringUTFChars(env, jhost, 0);
372 /* Ensure the property is valid */
373 if (psz_host == NULL || psz_host[0] == '\0')
374 goto end;
376 /* Fetch "http.proxyPort" property (only if "http.proxyHost" is valid) */
377 jkey = (*env)->NewStringUTF(env, "http.proxyPort");
378 if ((*env)->ExceptionCheck(env))
380 (*env)->ExceptionClear(env);
381 jkey = NULL;
383 if (jkey == NULL)
384 goto end;
386 jport = (*env)->CallStaticObjectMethod(env, fields.System.clazz,
387 fields.System.getProperty, jkey);
388 (*env)->DeleteLocalRef(env, jkey);
390 /* Ensure the property is valid */
391 if (jport != NULL)
393 psz_port = (*env)->GetStringUTFChars(env, jport, 0);
394 if (psz_port != NULL && (psz_port[0] == '\0' || psz_port[0] == '0'))
396 (*env)->ReleaseStringUTFChars(env, jport, psz_port);
397 psz_port = NULL;
401 /* Concat "http://" "http.proxyHost" and "http.proxyPort" */
402 if (asprintf(&psz_ret, "http://%s%s%s",
403 psz_host,
404 psz_port != NULL ? ":" : "",
405 psz_port != NULL ? psz_port : "") == -1)
406 psz_ret = NULL;
408 end:
409 if (psz_host != NULL)
410 (*env)->ReleaseStringUTFChars(env, jhost, psz_host);
411 if (jhost != NULL)
412 (*env)->DeleteLocalRef(env, jhost);
413 if (psz_port != NULL)
414 (*env)->ReleaseStringUTFChars(env, jport, psz_port);
415 if (jport != NULL)
416 (*env)->DeleteLocalRef(env, jport);
417 release_env(b_detach);
418 return psz_ret;