Android: Implement open and save file dialog.

This commit is contained in:
Miku AuahDark 2024-05-06 12:14:20 +08:00 committed by Sam Lantinga
parent ea1904eda1
commit 86fada6faa
7 changed files with 312 additions and 3 deletions

View file

@ -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 */