|
@@ -177,6 +177,10 @@ JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)(
|
|
|
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)(
|
|
|
JNIEnv *env, jclass jcls);
|
|
|
|
|
|
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)(
|
|
|
+ JNIEnv *env, jclass jcls,
|
|
|
+ jint requestCode, jobjectArray fileList, jint filter);
|
|
|
+
|
|
|
static JNINativeMethod SDLActivity_tab[] = {
|
|
|
{ "nativeGetVersion", "()Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetVersion) },
|
|
|
{ "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) },
|
|
@@ -211,7 +215,8 @@ static JNINativeMethod SDLActivity_tab[] = {
|
|
|
{ "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) },
|
|
|
{ "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) },
|
|
|
{ "nativeAllowRecreateActivity", "()Z", SDL_JAVA_INTERFACE(nativeAllowRecreateActivity) },
|
|
|
- { "nativeCheckSDLThreadCounter", "()I", SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter) }
|
|
|
+ { "nativeCheckSDLThreadCounter", "()I", SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter) },
|
|
|
+ { "onNativeFileDialog", "(I[Ljava/lang/String;I)V", SDL_JAVA_INTERFACE(onNativeFileDialog) }
|
|
|
};
|
|
|
|
|
|
/* Java class SDLInputConnection */
|
|
@@ -346,6 +351,7 @@ static jmethodID midShouldMinimizeOnFocusLoss;
|
|
|
static jmethodID midShowTextInput;
|
|
|
static jmethodID midSupportsRelativeMouse;
|
|
|
static jmethodID midOpenFileDescriptor;
|
|
|
+static jmethodID midShowFileDialog;
|
|
|
|
|
|
/* audio manager */
|
|
|
static jclass mAudioManagerClass;
|
|
@@ -640,6 +646,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
|
|
|
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
|
|
|
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
|
|
|
midOpenFileDescriptor = (*env)->GetStaticMethodID(env, mActivityClass, "openFileDescriptor", "(Ljava/lang/String;Ljava/lang/String;)I");
|
|
|
+ midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZZI)Z");
|
|
|
|
|
|
if (!midClipboardGetText ||
|
|
|
!midClipboardHasText ||
|
|
@@ -670,7 +677,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
|
|
|
!midShouldMinimizeOnFocusLoss ||
|
|
|
!midShowTextInput ||
|
|
|
!midSupportsRelativeMouse ||
|
|
|
- !midOpenFileDescriptor) {
|
|
|
+ !midOpenFileDescriptor ||
|
|
|
+ !midShowFileDialog) {
|
|
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
|
|
|
}
|
|
|
|
|
@@ -2823,4 +2831,138 @@ int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode)
|
|
|
return fd;
|
|
|
}
|
|
|
|
|
|
+static struct AndroidFileDialog
|
|
|
+{
|
|
|
+ int request_code;
|
|
|
+ SDL_DialogFileCallback callback;
|
|
|
+ void *userdata;
|
|
|
+} mAndroidFileDialogData;
|
|
|
+
|
|
|
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)(
|
|
|
+ JNIEnv *env, jclass jcls,
|
|
|
+ jint requestCode, jobjectArray fileList, jint filter)
|
|
|
+{
|
|
|
+ if (mAndroidFileDialogData.callback != NULL && mAndroidFileDialogData.request_code == requestCode) {
|
|
|
+ if (fileList == NULL) {
|
|
|
+ SDL_SetError("Unspecified error in JNI");
|
|
|
+ mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
|
|
|
+ mAndroidFileDialogData.callback = NULL;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Convert fileList to string */
|
|
|
+ size_t count = (*env)->GetArrayLength(env, fileList);
|
|
|
+ char **charFileList = SDL_calloc(sizeof(char*), count + 1);
|
|
|
+
|
|
|
+ if (charFileList == NULL) {
|
|
|
+ SDL_OutOfMemory();
|
|
|
+ mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
|
|
|
+ mAndroidFileDialogData.callback = NULL;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Convert to UTF-8 */
|
|
|
+ /* TODO: Fix modified UTF-8 to classic UTF-8 */
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ jstring string = (*env)->GetObjectArrayElement(env, fileList, i);
|
|
|
+ if (!string) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char *utf8string = (*env)->GetStringUTFChars(env, string, NULL);
|
|
|
+ if (!utf8string) {
|
|
|
+ (*env)->DeleteLocalRef(env, string);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ char *newFile = SDL_strdup(utf8string);
|
|
|
+ if (!newFile) {
|
|
|
+ (*env)->ReleaseStringUTFChars(env, string, utf8string);
|
|
|
+ (*env)->DeleteLocalRef(env, string);
|
|
|
+ SDL_OutOfMemory();
|
|
|
+ mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1);
|
|
|
+ mAndroidFileDialogData.callback = NULL;
|
|
|
+
|
|
|
+ /* Cleanup memory */
|
|
|
+ for (int j = 0; j < i; j++) {
|
|
|
+ SDL_free(charFileList[j]);
|
|
|
+ }
|
|
|
+ SDL_free(charFileList);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ charFileList[i] = newFile;
|
|
|
+ (*env)->ReleaseStringUTFChars(env, string, utf8string);
|
|
|
+ (*env)->DeleteLocalRef(env, string);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Call user-provided callback */
|
|
|
+ SDL_ClearError();
|
|
|
+ mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, (const char *const *) charFileList, filter);
|
|
|
+ mAndroidFileDialogData.callback = NULL;
|
|
|
+
|
|
|
+ /* Cleanup memory */
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ SDL_free(charFileList[i]);
|
|
|
+ }
|
|
|
+ SDL_free(charFileList);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+SDL_bool Android_JNI_OpenFileDialog(
|
|
|
+ SDL_DialogFileCallback callback, void* userdata,
|
|
|
+ const SDL_DialogFileFilter *filters, SDL_bool forwrite, SDL_bool multiple)
|
|
|
+{
|
|
|
+ if (mAndroidFileDialogData.callback != NULL) {
|
|
|
+ SDL_SetError("Only one file dialog can be run at a time.");
|
|
|
+ return SDL_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (forwrite) {
|
|
|
+ multiple = SDL_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ JNIEnv *env = Android_JNI_GetEnv();
|
|
|
+
|
|
|
+ /* Setup filters */
|
|
|
+ jobjectArray filtersArray = NULL;
|
|
|
+ if (filters) {
|
|
|
+ /* Count how many filters */
|
|
|
+ int count = 0;
|
|
|
+ for (const SDL_DialogFileFilter *f = filters; f->name != NULL && f->pattern != NULL; f++) {
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+
|
|
|
+ jclass stringClass = (*env)->FindClass(env, "java/lang/String");
|
|
|
+ filtersArray = (*env)->NewObjectArray(env, count, stringClass, NULL);
|
|
|
+
|
|
|
+ /* Convert to string */
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ jstring str = (*env)->NewStringUTF(env, filters[i].pattern);
|
|
|
+ (*env)->SetObjectArrayElement(env, filtersArray, i, str);
|
|
|
+ (*env)->DeleteLocalRef(env, str);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Setup data */
|
|
|
+ static SDL_AtomicInt next_request_code;
|
|
|
+ mAndroidFileDialogData.request_code = SDL_AtomicAdd(&next_request_code, 1);
|
|
|
+ mAndroidFileDialogData.userdata = userdata;
|
|
|
+ mAndroidFileDialogData.callback = callback;
|
|
|
+
|
|
|
+ /* Invoke JNI */
|
|
|
+ jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass,
|
|
|
+ midShowFileDialog, filtersArray, (jboolean) multiple, (jboolean) forwrite, mAndroidFileDialogData.request_code);
|
|
|
+ (*env)->DeleteLocalRef(env, filtersArray);
|
|
|
+ if (!success) {
|
|
|
+ mAndroidFileDialogData.callback = NULL;
|
|
|
+ SDL_AtomicAdd(&next_request_code, -1);
|
|
|
+ SDL_SetError("Unspecified error in JNI");
|
|
|
+
|
|
|
+ return SDL_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return SDL_TRUE;
|
|
|
+}
|
|
|
+
|
|
|
#endif /* SDL_PLATFORM_ANDROID */
|