diff --git a/src/api-impl-jni/handle_cache.c b/src/api-impl-jni/handle_cache.c index ae0c20a4..073bc436 100644 --- a/src/api-impl-jni/handle_cache.c +++ b/src/api-impl-jni/handle_cache.c @@ -99,6 +99,7 @@ void set_up_handle_cache(JNIEnv *env) (*env)->ExceptionDescribe(env); 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.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.application.class = _REF((*env)->FindClass(env, "android/app/Application")); diff --git a/src/api-impl-jni/handle_cache.h b/src/api-impl-jni/handle_cache.h index 84123504..462e3fb4 100644 --- a/src/api-impl-jni/handle_cache.h +++ b/src/api-impl-jni/handle_cache.h @@ -97,6 +97,7 @@ struct handle_cache { jmethodID get_package_name; jmethodID sendBroadcast; jmethodID startActivity; + jmethodID resolveActivityInternal; jmethodID startService; } context; struct { diff --git a/src/api-impl-jni/views/android_view_View.c b/src/api-impl-jni/views/android_view_View.c index 0955d2af..71145d7e 100644 --- a/src/api-impl-jni/views/android_view_View.c +++ b/src/api-impl-jni/views/android_view_View.c @@ -158,6 +158,8 @@ static gboolean on_event(GtkEventControllerLegacy *event_controller, GdkEvent *e } 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 */ if(id == 0) diff --git a/src/api-impl/android/content/Context.java b/src/api-impl/android/content/Context.java index 06343b61..6e61d20e 100644 --- a/src/api-impl/android/content/Context.java +++ b/src/api-impl/android/content/Context.java @@ -547,6 +547,30 @@ public class Context extends Object { 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) { Slog.i(TAG, "startActivity(" + intent + ") called"); if (intent.getAction() != null && intent.getAction().equals("android.intent.action.CHOOSER")) { diff --git a/src/main-executable/main.c b/src/main-executable/main.c index ca27a3f7..d7cfbfe8 100644 --- a/src/main-executable/main.c +++ b/src/main-executable/main.c @@ -236,6 +236,31 @@ static void parse_string_extras(JNIEnv *env, char **extra_string_keys, jobject i 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) { // 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_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)); // set package name as application id for window icon on Wayland. Needs a {package_name}.desktop file defining the icon