handle drag-and-drop events as ACTION_SEND Intents

This commit is contained in:
Julian Winkler
2025-07-14 12:25:04 +02:00
parent 0064677755
commit b7843665ef
5 changed files with 56 additions and 0 deletions

View File

@@ -99,6 +99,7 @@ void set_up_handle_cache(JNIEnv *env)
(*env)->ExceptionDescribe(env); (*env)->ExceptionDescribe(env);
handle_cache.context.sendBroadcast = _METHOD(handle_cache.context.class, "sendBroadcast", "(Landroid/content/Intent;)V"); handle_cache.context.sendBroadcast = _METHOD(handle_cache.context.class, "sendBroadcast", "(Landroid/content/Intent;)V");
handle_cache.context.startActivity = _METHOD(handle_cache.context.class, "startActivity", "(Landroid/content/Intent;)V"); handle_cache.context.startActivity = _METHOD(handle_cache.context.class, "startActivity", "(Landroid/content/Intent;)V");
handle_cache.context.resolveActivityInternal = _STATIC_METHOD(handle_cache.context.class, "resolveActivityInternal", "(Landroid/content/Intent;)Landroid/app/Activity;");
handle_cache.context.startService = _METHOD(handle_cache.context.class, "startService", "(Landroid/content/Intent;)Landroid/content/ComponentName;"); handle_cache.context.startService = _METHOD(handle_cache.context.class, "startService", "(Landroid/content/Intent;)Landroid/content/ComponentName;");
handle_cache.application.class = _REF((*env)->FindClass(env, "android/app/Application")); handle_cache.application.class = _REF((*env)->FindClass(env, "android/app/Application"));

View File

@@ -97,6 +97,7 @@ struct handle_cache {
jmethodID get_package_name; jmethodID get_package_name;
jmethodID sendBroadcast; jmethodID sendBroadcast;
jmethodID startActivity; jmethodID startActivity;
jmethodID resolveActivityInternal;
jmethodID startService; jmethodID startService;
} context; } context;
struct { struct {

View File

@@ -158,6 +158,8 @@ static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *e
} }
uintptr_t id = (uintptr_t)gdk_event_get_event_sequence(event); uintptr_t id = (uintptr_t)gdk_event_get_event_sequence(event);
if (id > MAX_POINTERS-1) // sequence id is a real pointer for drag and drop events
return false;
/* FIXME: this will clash with touchscreen */ /* FIXME: this will clash with touchscreen */
if(id == 0) if(id == 0)

View File

@@ -547,6 +547,30 @@ public class Context extends Object {
return true; return true;
} }
/* For use from native code */
static Activity resolveActivityInternal(Intent intent) throws ReflectiveOperationException {
String className = null;
if (intent.getComponent() != null) {
className = intent.getComponent().getClassName();
} else {
int best_score = -5;
for (PackageParser.Activity activity: pkg.activities) {
for (PackageParser.IntentInfo intentInfo: activity.intents) {
int score = intentInfo.match(intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), "Context");
if (score > best_score && score > 0) {
className = activity.className;
best_score = score;
}
}
}
}
if (className != null) {
return Activity.internalCreateActivity(className, this_application.native_window, intent);
} else {
return null;
}
}
public void startActivity(Intent intent) { public void startActivity(Intent intent) {
Slog.i(TAG, "startActivity(" + intent + ") called"); Slog.i(TAG, "startActivity(" + intent + ") called");
if (intent.getAction() != null && intent.getAction().equals("android.intent.action.CHOOSER")) { if (intent.getAction() != null && intent.getAction().equals("android.intent.action.CHOOSER")) {

View File

@@ -236,6 +236,31 @@ static void parse_string_extras(JNIEnv *env, char **extra_string_keys, jobject i
g_regex_unref(regex); g_regex_unref(regex);
} }
/* Drag and drop callback to simulate ACTION_SEND intents */
static gboolean on_drop(GtkDropTarget *target, const GValue *value, double x, double y, gpointer user_data)
{
const char *data = g_value_get_string(value);
if (!data || !*data) {
return FALSE;
}
JNIEnv *env = get_jni_env();
jobject intent = (*env)->NewObject(env, handle_cache.intent.class, handle_cache.intent.constructor);
_SET_OBJ_FIELD(intent, "action", "Ljava/lang/String;", _JSTRING("android.intent.action.SEND"));
_SET_OBJ_FIELD(intent, "data", "Landroid/net/Uri;", (*env)->CallStaticObjectMethod(env, handle_cache.uri.class, handle_cache.uri.parse, _JSTRING(data)));
jobject activity = (*env)->CallStaticObjectMethod(env, handle_cache.context.class, handle_cache.context.resolveActivityInternal, intent);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
}
if (!activity) {
printf("failed to resolve activity to handle URI: %s\n", data);
return FALSE;
}
activity_start(env, activity);
return TRUE;
}
static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *hint, struct jni_callback_data *d) static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *hint, struct jni_callback_data *d)
{ {
// TODO: pass all files to classpath // TODO: pass all files to classpath
@@ -668,6 +693,9 @@ static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *h
gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), back_button); gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), back_button);
gtk_window_set_titlebar(GTK_WINDOW(window), header_bar); gtk_window_set_titlebar(GTK_WINDOW(window), header_bar);
GtkDropTarget *drop_target = gtk_drop_target_new(G_TYPE_STRING, GDK_ACTION_COPY);
g_signal_connect(drop_target, "drop", G_CALLBACK(on_drop), NULL);
gtk_widget_add_controller(window, GTK_EVENT_CONTROLLER(drop_target));
gtk_window_present(GTK_WINDOW(window)); gtk_window_present(GTK_WINDOW(window));
// set package name as application id for window icon on Wayland. Needs a {package_name}.desktop file defining the icon // set package name as application id for window icon on Wayland. Needs a {package_name}.desktop file defining the icon