From 4ec150c802e29e6268954f563ffc65f149e3fd32 Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Mon, 7 Jul 2025 16:41:10 +0200 Subject: [PATCH] separate Intent serialization and action handling out of the NotificationManager This way, the mechanism can in the future also be used to send Intents from one app to another. --- meson.build | 1 + .../app/android_app_NotificationManager.c | 60 +++++-------------- .../android_app_NotificationManager.h | 8 +-- src/api-impl-jni/util.c | 60 +++++++++++++++++++ src/api-impl-jni/util.h | 13 ++++ .../android/app/NotificationManager.java | 44 +++----------- src/main-executable/actions.c | 49 +++++++++++++++ src/main-executable/actions.h | 4 ++ src/main-executable/main.c | 22 +++++-- 9 files changed, 171 insertions(+), 90 deletions(-) create mode 100644 src/main-executable/actions.c create mode 100644 src/main-executable/actions.h diff --git a/meson.build b/meson.build index 938f9ae1..d81a7e75 100644 --- a/meson.build +++ b/meson.build @@ -193,6 +193,7 @@ resources = gnome.compile_resources('com.gitlab.android_translation_layer.androi executable('android-translation-layer', [ 'src/main-executable/main.c', + 'src/main-executable/actions.c', 'src/main-executable/back_button.c', 'src/main-executable/bionic_compat.c', 'src/main-executable/libc_bio_path_overrides.c', diff --git a/src/api-impl-jni/app/android_app_NotificationManager.c b/src/api-impl-jni/app/android_app_NotificationManager.c index fe05424f..bafcdd9e 100644 --- a/src/api-impl-jni/app/android_app_NotificationManager.c +++ b/src/api-impl-jni/app/android_app_NotificationManager.c @@ -16,14 +16,15 @@ static GHashTable *ongoing_notifications = NULL; /* We queue up notification updates in pending_notifications to make sure that there is at least 200ms delay between consecutive updates. This prevents dynamic notification updated from arriving in wrong - order at the desktop environment */ + order at the desktop environment. + Normally 20ms should be enough to prevent notification update order issues, but we use a 10x larger value + to be safe and 200ms should be more than enough as notification update interval. */ static GHashTable *pending_notifications = NULL; static GMutex pending_notifications_mutex = {0}; static GSource *send_notifcation_timer = NULL; static gboolean send_notifcation_func(GSource *send_notifcation_timer, GSourceFunc callback, gpointer user_data) { - printf("Sending notifications\n"); GApplication *app = g_application_get_default(); GHashTableIter iter; gpointer key, value; @@ -58,20 +59,6 @@ static void unref_nullsafe(void *data) { g_object_unref(data); } -static void notification_action(GSimpleAction *action, GVariant* parameter, gpointer user_data) -{ - printf("notification_action\n"); - int type; - const char *actionName; - const char *className; - const char *data; - JNIEnv *env = get_jni_env(); - - g_variant_get(parameter, "(isss)", &type, &actionName, &className, &data); - jmethodID notificationActionCallback = _STATIC_METHOD((*env)->FindClass(env, "android/app/NotificationManager"), "notificationActionCallback", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - (*env)->CallStaticVoidMethod(env, (*env)->FindClass(env, "android/app/NotificationManager"), notificationActionCallback, type, _JSTRING(actionName), _JSTRING(className), _JSTRING(data)); -} - static void queue_notification(int id, GNotification *notification) { g_mutex_lock(&pending_notifications_mutex); if (!pending_notifications) { @@ -81,22 +68,21 @@ static void queue_notification(int id, GNotification *notification) { GApplication *app = g_application_get_default(); gchar *desktop_id = g_strdup_printf("%s.desktop", g_application_get_application_id(app)); GDesktopAppInfo *info = g_desktop_app_info_new(desktop_id); - if (!info) // some desktop environments don't allow XDG-portal notifications without a desktop file + /* Some desktop environments don't allow XDG-portal notifications without a desktop file. + There is no public API to force a specific backend, so we have to set the environment variable. + The GNOTIFICATION_BACKEND variable will be read by GIO the first time the notification backend is used. + This method should be future proof unless the freedesktop backend is removed. */ + if (!info) setenv("GNOTIFICATION_BACKEND", "freedesktop", 0); else g_object_unref(info); g_free(desktop_id); - GSimpleAction *action = g_simple_action_new("notificationaction", g_variant_type_new("(isss)")); - g_signal_connect(action, "activate", G_CALLBACK(notification_action), app); - g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(action)); - g_object_unref(action); ongoing_notifications = g_hash_table_new(NULL, NULL); } g_hash_table_insert(pending_notifications, GINT_TO_POINTER(id), notification); g_mutex_unlock(&pending_notifications_mutex); - if (g_source_get_ready_time(send_notifcation_timer) == -1) { - g_source_set_ready_time(send_notifcation_timer, 0); // immediately - } + if (g_source_get_ready_time(send_notifcation_timer) == -1) + g_source_set_ready_time(send_notifcation_timer, 0); // immediately } JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder(JNIEnv *env, jobject this) @@ -104,32 +90,18 @@ JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder(J return _INTPTR(g_notification_new("")); } -static GVariant *serialize_intent(JNIEnv *env, jint type, jstring action_jstr, jstring className_jstr, jstring data_jstr) -{ - const char *action = action_jstr ? (*env)->GetStringUTFChars(env, action_jstr, NULL) : NULL; - const char *className = className_jstr ? (*env)->GetStringUTFChars(env, className_jstr, NULL) : NULL; - const char *data = data_jstr ? (*env)->GetStringUTFChars(env, data_jstr, NULL) : NULL; - GVariant *intent = g_variant_new("(isss)", type, action ?: "", className ?: "", data ?: ""); - if (action_jstr) (*env)->ReleaseStringUTFChars(env, action_jstr, action); - if (className_jstr) (*env)->ReleaseStringUTFChars(env, className_jstr, className); - if (data_jstr) (*env)->ReleaseStringUTFChars(env, data_jstr, data); - return intent; -} - -JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction(JNIEnv *env, jobject this, jlong builder_ptr, jstring name_jstr, jint type, jstring action, jstring className, jstring data) +JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction(JNIEnv *env, jobject this, jlong builder_ptr, jstring name_jstr, jint type, jobject intent) { GNotification *notification = _PTR(builder_ptr); const char *name = ""; - if (name_jstr) { + if (name_jstr) name = (*env)->GetStringUTFChars(env, name_jstr, NULL); - } - g_notification_add_button_with_target_value(notification, name, "app.notificationaction", serialize_intent(env, type, action, className, data)); - if (name_jstr) { + g_notification_add_button_with_target_value(notification, name, intent_actionname_from_type(type), intent_serialize(env, intent)); + if (name_jstr) (*env)->ReleaseStringUTFChars(env, name_jstr, name); - } } -JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotification(JNIEnv *env, jobject this, jlong builder_ptr, jint id, jstring title_jstr, jstring text_jstr, jstring icon_jstr, jboolean ongoing, jint type, jstring action, jstring className, jstring data) +JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotification(JNIEnv *env, jobject this, jlong builder_ptr, jint id, jstring title_jstr, jstring text_jstr, jstring icon_jstr, jboolean ongoing, jint type, jobject intent) { GNotification *notification = _PTR(builder_ptr); @@ -157,7 +129,7 @@ JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotificati g_free(icon_path_full); (*env)->ReleaseStringUTFChars(env, icon_jstr, icon_path); } - g_notification_set_default_action_and_target_value(notification, "app.notificationaction", serialize_intent(env, type, action, className, data)); + g_notification_set_default_action_and_target_value(notification, intent_actionname_from_type(type), intent_serialize(env, intent)); queue_notification(id, notification); if (ongoing) g_hash_table_add(ongoing_notifications, GINT_TO_POINTER(id)); diff --git a/src/api-impl-jni/generated_headers/android_app_NotificationManager.h b/src/api-impl-jni/generated_headers/android_app_NotificationManager.h index 9cf344a8..8722b89f 100644 --- a/src/api-impl-jni/generated_headers/android_app_NotificationManager.h +++ b/src/api-impl-jni/generated_headers/android_app_NotificationManager.h @@ -18,18 +18,18 @@ JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder /* * Class: android_app_NotificationManager * Method: nativeAddAction - * Signature: (JLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + * Signature: (JLjava/lang/String;ILandroid/content/Intent;)V */ JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction - (JNIEnv *, jobject, jlong, jstring, jint, jstring, jstring, jstring); + (JNIEnv *, jobject, jlong, jstring, jint, jobject); /* * Class: android_app_NotificationManager * Method: nativeShowNotification - * Signature: (JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + * Signature: (JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILandroid/content/Intent;)V */ JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotification - (JNIEnv *, jobject, jlong, jint, jstring, jstring, jstring, jboolean, jint, jstring, jstring, jstring); + (JNIEnv *, jobject, jlong, jint, jstring, jstring, jstring, jboolean, jint, jobject); /* * Class: android_app_NotificationManager diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 0fc66fe5..666db971 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -159,6 +159,8 @@ void set_up_handle_cache(JNIEnv *env) if((*env)->ExceptionCheck(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.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.get_app_icon_path = _METHOD(handle_cache.application.class, "get_app_icon_path", "()Ljava/lang/String;"); @@ -179,6 +181,8 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "", "()V"); handle_cache.intent.putExtraCharSequence = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;"); handle_cache.intent.putExtraByteArray = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;[B)Landroid/content/Intent;"); + handle_cache.intent.getDataString = _METHOD(handle_cache.intent.class, "getDataString", "()Ljava/lang/String;"); + handle_cache.intent.setClassName = _METHOD(handle_cache.intent.class, "setClassName", "(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"); handle_cache.instrumentation.class = _REF((*env)->FindClass(env, "android/app/Instrumentation")); @@ -188,6 +192,9 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.canvas.class = _REF((*env)->FindClass(env, "android/graphics/Canvas")); handle_cache.canvas.drawText = _METHOD(handle_cache.canvas.class, "drawText", "(Ljava/lang/CharSequence;IIFFLandroid/graphics/Paint;)V"); + + handle_cache.uri.class = _REF((*env)->FindClass(env, "android/net/Uri")); + handle_cache.uri.parse = _STATIC_METHOD(handle_cache.uri.class, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); } void extract_from_apk(const char *path, const char *target) { @@ -371,3 +378,56 @@ void atl_safe_gtk_widget_queue_resize(GtkWidget *widget) gtk_widget_add_tick_callback(widget, queue_queue_resize, NULL, NULL); } } + +GVariant *intent_serialize(JNIEnv *env, jobject intent) { + jstring action_jstr = _GET_OBJ_FIELD(intent, "action", "Ljava/lang/String;"); + jobject component = _GET_OBJ_FIELD(intent, "component", "Landroid/content/ComponentName;"); + jstring className_jstr = component ? _GET_OBJ_FIELD(component, "mClass", "Ljava/lang/String;") : NULL; + jstring data_jstr = (*env)->CallObjectMethod(env, intent, handle_cache.intent.getDataString); + + const char *action = action_jstr ? (*env)->GetStringUTFChars(env, action_jstr, NULL) : NULL; + const char *className = className_jstr ? (*env)->GetStringUTFChars(env, className_jstr, NULL) : NULL; + const char *data = data_jstr ? (*env)->GetStringUTFChars(env, data_jstr, NULL) : NULL; + GVariant *variant = g_variant_new(INTENT_G_VARIANT_TYPE_STRING, action ?: "", className ?: "", data ?: ""); + if (action_jstr) + (*env)->ReleaseStringUTFChars(env, action_jstr, action); + if (className_jstr) + (*env)->ReleaseStringUTFChars(env, className_jstr, className); + if (data_jstr) + (*env)->ReleaseStringUTFChars(env, data_jstr, data); + return variant; +} + +jobject intent_deserialize(JNIEnv *env, GVariant *variant) { + const char *action; + const char *className; + const char *data; + g_variant_get(variant, INTENT_G_VARIANT_TYPE_STRING, &action, &className, &data); + if (action && action[0] == '\0') + action = NULL; + if (className && className[0] == '\0') + className = NULL; + if (data && data[0] == '\0') + data = NULL; + + jobject intent = (*env)->NewObject(env, handle_cache.intent.class, handle_cache.intent.constructor); + _SET_OBJ_FIELD(intent, "action", "Ljava/lang/String;", _JSTRING(action)); + if (className) + (*env)->CallObjectMethod(env, intent, handle_cache.intent.setClassName, _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"), _JSTRING(className)); + if (data) + _SET_OBJ_FIELD(intent, "data", "Landroid/net/Uri;", (*env)->CallStaticObjectMethod(env, handle_cache.uri.class, handle_cache.uri.parse, _JSTRING(data))); + return intent; +} + +const char *intent_actionname_from_type(int type) { + switch (type) { + case 0: + return "app.startActivity"; + case 1: + return "app.startService"; + case 2: + return "app.sendBroadcast"; + default: + return NULL; + } +} diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index 613d7636..b507599b 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -103,6 +103,8 @@ struct handle_cache { jclass class; jmethodID get_package_name; jmethodID sendBroadcast; + jmethodID startActivity; + jmethodID startService; } context; struct { jclass class; @@ -128,6 +130,8 @@ struct handle_cache { jmethodID constructor; jmethodID putExtraCharSequence; jmethodID putExtraByteArray; + jmethodID getDataString; + jmethodID setClassName; } intent; struct { jclass class; @@ -143,6 +147,10 @@ struct handle_cache { jclass class; jmethodID drawText; } canvas; + struct { + jclass class; + jmethodID parse; + } uri; }; extern struct handle_cache handle_cache; @@ -195,4 +203,9 @@ void atl_safe_gtk_widget_set_visible(GtkWidget *widget, gboolean visible); void atl_safe_gtk_widget_queue_allocate(GtkWidget *widget); void atl_safe_gtk_widget_queue_resize(GtkWidget *widget); +#define INTENT_G_VARIANT_TYPE_STRING "(sss)" // (action, className, data) +GVariant *intent_serialize(JNIEnv *env, jobject intent); +jobject intent_deserialize(JNIEnv *env, GVariant *variant); +const char *intent_actionname_from_type(int type); + #endif diff --git a/src/api-impl/android/app/NotificationManager.java b/src/api-impl/android/app/NotificationManager.java index 0e1849bc..c333b391 100644 --- a/src/api-impl/android/app/NotificationManager.java +++ b/src/api-impl/android/app/NotificationManager.java @@ -4,10 +4,8 @@ import java.util.Collections; import java.util.List; import android.app.Notification.MediaStyle; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -30,28 +28,20 @@ public class NotificationManager { long builder = nativeInitBuilder(); for (Notification.Action action : notification.actions) { int intentType = -1; - String actionName = null; - String className = null; - String data = null; + Intent intent = null; if (action.intent != null) { intentType = action.intent.type; - actionName = action.intent.intent.getAction(); - className = action.intent.intent.getComponent() != null ? action.intent.intent.getComponent().getClassName() : null; - data = action.intent.intent.getData() != null ? action.intent.intent.getData().toString() : null; + intent = action.intent.intent; } - nativeAddAction(builder, action.title, intentType, actionName, className, data); + nativeAddAction(builder, action.title, intentType, intent); } int intentType = -1; - String actionName = null; - String className = null; - String data = null; + Intent intent = null; if (notification.intent != null) { intentType = notification.intent.type; - actionName = notification.intent.intent.getAction(); - className = notification.intent.intent.getComponent() != null ? notification.intent.intent.getComponent().getClassName() : null; - data = notification.intent.intent.getData() != null ? notification.intent.intent.getData().toString() : null; + intent = notification.intent.intent; } - nativeShowNotification(builder, id, notification.title, notification.text, notification.iconPath, notification.ongoing, intentType, actionName, className, data); + nativeShowNotification(builder, id, notification.title, notification.text, notification.iconPath, notification.ongoing, intentType, intent); } public void notify(int id, Notification notification) { @@ -78,29 +68,11 @@ public class NotificationManager { cancel(null, id); } - protected static void notificationActionCallback(int intentType, String action, String className, String data) { - Context context = Context.this_application; - action = "".equals(action) ? null : action; - className = "".equals(className) ? null : className; - data = "".equals(data) ? null : data; - Intent intent = new Intent(action, data != null ? Uri.parse(data) : null); - if (className != null) { - intent.setComponent(new ComponentName(context, className)); - } - if (intentType == 0) { // type Activity - context.startActivity(intent); - } else if (intentType == 1) { // type Service - context.startService(intent); - } else if (intentType == 2) { // type Broadcast - context.sendBroadcast(intent); - } - } - public void createNotificationChannel(NotificationChannel channel) {} protected native long nativeInitBuilder(); - protected native void nativeAddAction(long builder, String title, int intentType, String action, String className, String data); - protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, String action, String className, String data); + protected native void nativeAddAction(long builder, String title, int intentType, Intent intent); + protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, Intent intent); protected native void nativeShowMPRIS(String packageName, String identiy); protected native void nativeCancel(int id); protected native void nativeCancelMPRIS(); diff --git a/src/main-executable/actions.c b/src/main-executable/actions.c new file mode 100644 index 00000000..7d762b46 --- /dev/null +++ b/src/main-executable/actions.c @@ -0,0 +1,49 @@ +#include +#include + +#include "../api-impl-jni/defines.h" +#include "../api-impl-jni/util.h" + +static void action_start_activity(GSimpleAction *action, GVariant* parameter, gpointer user_data) +{ + JNIEnv *env = get_jni_env(); + + jobject intent = intent_deserialize(env, parameter); + jobject context = _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"); + (*env)->CallVoidMethod(env, context, handle_cache.context.startActivity, intent); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + } +} + +static void action_start_service(GSimpleAction *action, GVariant* parameter, gpointer user_data) +{ + JNIEnv *env = get_jni_env(); + + jobject intent = intent_deserialize(env, parameter); + jobject context = _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"); + (*env)->CallObjectMethod(env, context, handle_cache.context.startService, intent); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + } +} + +static void action_send_broadcast(GSimpleAction *action, GVariant* parameter, gpointer user_data) +{ + JNIEnv *env = get_jni_env(); + + jobject intent = intent_deserialize(env, parameter); + jobject context = _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"); + (*env)->CallVoidMethod(env, context, handle_cache.context.sendBroadcast, intent); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + } +} + +const GActionEntry action_entries[] = { + { "startActivity", action_start_activity, INTENT_G_VARIANT_TYPE_STRING}, + { "startService", action_start_service, INTENT_G_VARIANT_TYPE_STRING}, + { "sendBroadcast", action_send_broadcast, INTENT_G_VARIANT_TYPE_STRING}, +}; + +const int action_entries_count = ARRAY_SIZE(action_entries); diff --git a/src/main-executable/actions.h b/src/main-executable/actions.h new file mode 100644 index 00000000..23489d30 --- /dev/null +++ b/src/main-executable/actions.h @@ -0,0 +1,4 @@ +#include + +extern const GActionEntry action_entries[]; +extern const int action_entries_count; \ No newline at end of file diff --git a/src/main-executable/main.c b/src/main-executable/main.c index 320ac33d..a7dd10e3 100644 --- a/src/main-executable/main.c +++ b/src/main-executable/main.c @@ -12,6 +12,7 @@ #include "back_button.h" #include "libc_bio_path_overrides.h" +#include "actions.h" #include #include @@ -714,6 +715,19 @@ static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *h if ((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); } + + const char *app_id = g_application_get_application_id(G_APPLICATION(app)); + if (strcmp(app_id, "com.example.demo_application")) { + // This would normally happen automatically, if the GApplication is not contructed with G_APPLICATION_NON_UNIQUE + g_dbus_connection_call(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RequestName", + g_variant_new("(su)", app_id, G_BUS_NAME_OWNER_FLAGS_NONE), + G_VARIANT_TYPE("(u)"), + 0, -1, NULL, NULL, NULL); + } } static void activate(GtkApplication *app, struct jni_callback_data *d) @@ -780,12 +794,7 @@ int main(int argc, char **argv) callback_data->extra_jvm_options = NULL; callback_data->extra_string_keys = NULL; - bool has_app_id = false; - for (int i = 1; i < argc; i++) { - if (strncmp(argv[i], "--gapplication-app-id", sizeof("--gapplication-app-id")-1) == 0) - has_app_id = true; - } - app = gtk_application_new("com.example.demo_application", (has_app_id ? 0 : G_APPLICATION_NON_UNIQUE) | G_APPLICATION_HANDLES_OPEN | G_APPLICATION_CAN_OVERRIDE_APP_ID); + app = gtk_application_new("com.example.demo_application", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN | G_APPLICATION_CAN_OVERRIDE_APP_ID); // cmdline related setup init_cmd_parameters(G_APPLICATION(app), callback_data); @@ -793,6 +802,7 @@ int main(int argc, char **argv) g_signal_connect(app, "activate", G_CALLBACK(activate), callback_data); g_signal_connect(app, "open", G_CALLBACK(open), callback_data); + g_action_map_add_action_entries(G_ACTION_MAP(app), action_entries, action_entries_count, NULL); status = g_application_run(G_APPLICATION(app), argc, argv); remove_ongoing_notifications(); g_object_unref(app);