You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
implement file chooser using GtkFileChooserNative
This commit is contained in:
@@ -177,3 +177,59 @@ JNIEXPORT void JNICALL Java_android_app_Activity_nativeOpenURI(JNIEnv *env, jcla
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern GtkWindow *window; // TODO: get this in a better way
|
extern GtkWindow *window; // TODO: get this in a better way
|
||||||
|
|
||||||
|
struct filechooser_callback_data { jobject activity; jint request_code; };
|
||||||
|
|
||||||
|
#define RESULT_OK -1
|
||||||
|
#define RESULT_CANCELED 0
|
||||||
|
static void on_filechooser_response(GtkNativeDialog *native, int response, struct filechooser_callback_data *data)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
jmethodID fileChooserResultCallback = _METHOD(handle_cache.activity.class, "fileChooserResultCallback", "(IIILjava/lang/String;)V");
|
||||||
|
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
|
GtkFileChooserAction action = gtk_file_chooser_get_action(chooser);
|
||||||
|
if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
|
GFile *file = gtk_file_chooser_get_file(chooser);
|
||||||
|
char *uri = g_file_get_uri(file);
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod(env, data->activity, fileChooserResultCallback, data->request_code, RESULT_OK, action, _JSTRING(uri));
|
||||||
|
if ((*env)->ExceptionCheck(env))
|
||||||
|
(*env)->ExceptionDescribe(env);
|
||||||
|
|
||||||
|
g_free(uri);
|
||||||
|
g_object_unref(file);
|
||||||
|
} else {
|
||||||
|
(*env)->CallVoidMethod(env, data->activity, fileChooserResultCallback, data->request_code, RESULT_CANCELED, action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(native);
|
||||||
|
_UNREF(data->activity);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeFileChooser(JNIEnv *env, jobject this, jint action, jstring type_jstring, jstring filename_jstring, jint request_code)
|
||||||
|
{
|
||||||
|
const char *chooser_title = ((char *[]){"Open File", "Save File", "Select Folder"})[action];
|
||||||
|
GtkFileChooserNative *native = gtk_file_chooser_native_new(chooser_title, window, action, NULL, NULL);
|
||||||
|
|
||||||
|
const char *type = type_jstring ? (*env)->GetStringUTFChars(env, type_jstring, NULL) : NULL;
|
||||||
|
if (type) {
|
||||||
|
GtkFileFilter *filter = gtk_file_filter_new();
|
||||||
|
gtk_file_filter_add_mime_type(filter, type);
|
||||||
|
gtk_file_filter_set_name(filter, type);
|
||||||
|
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(native), filter);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, type_jstring, type);
|
||||||
|
}
|
||||||
|
const char *filename = filename_jstring ? (*env)->GetStringUTFChars(env, filename_jstring, NULL) : NULL;
|
||||||
|
if (filename) {
|
||||||
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(native), filename);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, filename_jstring, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct filechooser_callback_data *callback_data = malloc(sizeof(struct filechooser_callback_data));
|
||||||
|
callback_data->activity = _REF(this);
|
||||||
|
callback_data->request_code = request_code;
|
||||||
|
g_signal_connect (native, "response", G_CALLBACK(on_filechooser_response), callback_data);
|
||||||
|
gtk_native_dialog_show (GTK_NATIVE_DIALOG (native));
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ JNIEXPORT void JNICALL Java_android_app_Activity_nativeStartActivity
|
|||||||
JNIEXPORT void JNICALL Java_android_app_Activity_nativeOpenURI
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeOpenURI
|
||||||
(JNIEnv *, jclass, jstring);
|
(JNIEnv *, jclass, jstring);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: android_app_Activity
|
||||||
|
* Method: nativeFileChooser
|
||||||
|
* Signature: (ILjava/lang/String;Ljava/lang/String;I)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_android_app_Activity_nativeFileChooser
|
||||||
|
(JNIEnv *, jobject, jint, jstring, jstring, jint);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.content.ContextWrapper;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -26,6 +27,7 @@ import java.io.InputStream;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Activity extends ContextWrapper implements Window.Callback {
|
public class Activity extends ContextWrapper implements Window.Callback {
|
||||||
@@ -254,8 +256,20 @@ public class Activity extends ContextWrapper implements Window.Callback {
|
|||||||
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
|
||||||
|
|
||||||
|
// the order must match GtkFileChooserAction enum
|
||||||
|
private static final List<String> FILE_CHOOSER_ACTIONS = Arrays.asList(
|
||||||
|
"android.intent.action.OPEN_DOCUMENT", // (0) GTK_FILE_CHOOSER_ACTION_OPEN
|
||||||
|
"android.intent.action.CREATE_DOCUMENT", // (1) GTK_FILE_CHOOSER_ACTION_SAVE
|
||||||
|
"android.intent.action.OPEN_DOCUMENT_TREE" // (2) GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
|
||||||
|
);
|
||||||
|
|
||||||
|
// callback from native code
|
||||||
|
protected void fileChooserResultCallback(int requestCode, int resultCode, int action, String uri) {
|
||||||
|
onActivityResult(requestCode, resultCode, new Intent(FILE_CHOOSER_ACTIONS.get(action), uri != null ? Uri.parse(uri) : null));
|
||||||
|
}
|
||||||
|
|
||||||
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
||||||
System.out.println("startActivityForResult(" + intent + ", " + requestCode + ") called, but we don't currently support multiple activities");
|
System.out.println("startActivityForResult(" + intent + ", " + requestCode + ") called");
|
||||||
if (intent.getComponent() != null) {
|
if (intent.getComponent() != null) {
|
||||||
try {
|
try {
|
||||||
Class<? extends Activity> cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Activity.class);
|
Class<? extends Activity> cls = Class.forName(intent.getComponent().getClassName()).asSubclass(Activity.class);
|
||||||
@@ -272,12 +286,14 @@ public class Activity extends ContextWrapper implements Window.Callback {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us
|
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent());
|
||||||
}
|
}
|
||||||
|
} else if (FILE_CHOOSER_ACTIONS.contains(intent.getAction())) {
|
||||||
|
nativeFileChooser(FILE_CHOOSER_ACTIONS.indexOf(intent.getAction()), intent.getType(), intent.getStringExtra("android.intent.extra.TITLE"), requestCode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
System.out.println("startActivityForResult: intent was not handled. Calling onActivityResult(RESULT_CANCELED).");
|
System.out.println("startActivityForResult: intent was not handled. Calling onActivityResult(RESULT_CANCELED).");
|
||||||
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent()); // RESULT_CANCELED is the only pre-defined return value, so hopefully it works out for us
|
onActivityResult(requestCode, 0 /*RESULT_CANCELED*/, new Intent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void startActivityForResult(Intent intent, int requestCode) {
|
public void startActivityForResult(Intent intent, int requestCode) {
|
||||||
@@ -426,4 +442,5 @@ public class Activity extends ContextWrapper implements Window.Callback {
|
|||||||
public static native void nativeRecreateActivity(Activity activity);
|
public static native void nativeRecreateActivity(Activity activity);
|
||||||
public static native void nativeStartActivity(Activity activity);
|
public static native void nativeStartActivity(Activity activity);
|
||||||
public static native void nativeOpenURI(String uri);
|
public static native void nativeOpenURI(String uri);
|
||||||
|
public native void nativeFileChooser(int action, String type, String title, int requestCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package android.content;
|
package android.content;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
public class ContentResolver {
|
public class ContentResolver {
|
||||||
public final void registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer) {
|
public final void registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer) {
|
||||||
@@ -15,4 +19,8 @@ public class ContentResolver {
|
|||||||
}
|
}
|
||||||
public final void registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer, int userHandle) {
|
public final void registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer, int userHandle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ParcelFileDescriptor openFileDescriptor(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
return ParcelFileDescriptor.open(new File(uri.uri), ParcelFileDescriptor.parseMode(mode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,4 +290,8 @@ public class Intent {
|
|||||||
public Parcelable[] getParcelableArrayExtra(String name) {
|
public Parcelable[] getParcelableArrayExtra(String name) {
|
||||||
return extras.getParcelableArray(name);
|
return extras.getParcelableArray(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class Uri implements Parcelable {
|
|||||||
|
|
||||||
public static final Uri EMPTY = new Uri();
|
public static final Uri EMPTY = new Uri();
|
||||||
|
|
||||||
private URI uri;
|
public URI uri;
|
||||||
|
|
||||||
public static Uri parse(String s) {
|
public static Uri parse(String s) {
|
||||||
Uri ret = new Uri();
|
Uri ret = new Uri();
|
||||||
@@ -160,6 +160,10 @@ public class Uri implements Parcelable {
|
|||||||
return uri.getPath();
|
return uri.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAuthority() {
|
||||||
|
return uri.getAuthority();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.valueOf(uri);
|
return String.valueOf(uri);
|
||||||
|
|||||||
@@ -17,10 +17,21 @@
|
|||||||
package android.os;
|
package android.os;
|
||||||
|
|
||||||
import static android.system.OsConstants.AF_UNIX;
|
import static android.system.OsConstants.AF_UNIX;
|
||||||
|
import static android.system.OsConstants.O_APPEND;
|
||||||
|
import static android.system.OsConstants.O_CLOEXEC;
|
||||||
|
import static android.system.OsConstants.O_CREAT;
|
||||||
|
import static android.system.OsConstants.O_RDONLY;
|
||||||
|
import static android.system.OsConstants.O_RDWR;
|
||||||
|
import static android.system.OsConstants.O_TRUNC;
|
||||||
|
import static android.system.OsConstants.O_WRONLY;
|
||||||
import static android.system.OsConstants.SEEK_SET;
|
import static android.system.OsConstants.SEEK_SET;
|
||||||
import static android.system.OsConstants.SOCK_STREAM;
|
import static android.system.OsConstants.SOCK_STREAM;
|
||||||
|
import static android.system.OsConstants.S_IROTH;
|
||||||
|
import static android.system.OsConstants.S_IRWXG;
|
||||||
|
import static android.system.OsConstants.S_IRWXU;
|
||||||
import static android.system.OsConstants.S_ISLNK;
|
import static android.system.OsConstants.S_ISLNK;
|
||||||
import static android.system.OsConstants.S_ISREG;
|
import static android.system.OsConstants.S_ISREG;
|
||||||
|
import static android.system.OsConstants.S_IWOTH;
|
||||||
|
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.OsConstants;
|
import android.system.OsConstants;
|
||||||
@@ -246,15 +257,30 @@ public class ParcelFileDescriptor implements Closeable {
|
|||||||
return pfd;
|
return pfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { /*
|
private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
|
||||||
if ((mode & MODE_READ_WRITE) == 0) {
|
int flags = O_CLOEXEC;
|
||||||
throw new IllegalArgumentException(
|
if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE)
|
||||||
"Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
|
flags |= O_RDWR;
|
||||||
|
else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY)
|
||||||
|
flags |= O_WRONLY;
|
||||||
|
else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY)
|
||||||
|
flags |= O_RDONLY;
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Bad mode: " + mode);
|
||||||
|
if ((mode & MODE_CREATE) == MODE_CREATE)
|
||||||
|
flags |= O_CREAT;
|
||||||
|
if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE)
|
||||||
|
flags |= O_TRUNC;
|
||||||
|
if ((mode & MODE_APPEND) == MODE_APPEND)
|
||||||
|
flags |= O_APPEND;
|
||||||
|
int realMode = S_IRWXU | S_IRWXG;
|
||||||
|
if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
|
||||||
|
if ((mode & MODE_WORLD_WRITEABLE) != 0) realMode |= S_IWOTH;
|
||||||
|
try {
|
||||||
|
return android.system.Os.open(file.getPath(), flags, realMode);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
throw new FileNotFoundException(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
final String path = file.getPath();
|
|
||||||
return Parcel.openFileDescriptor(path, mode);*/
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user