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.
This commit is contained in:
Julian Winkler
2025-07-07 16:41:10 +02:00
parent 3708cc990e
commit 4ec150c802
9 changed files with 171 additions and 90 deletions

View File

@@ -193,6 +193,7 @@ resources = gnome.compile_resources('com.gitlab.android_translation_layer.androi
executable('android-translation-layer', [ executable('android-translation-layer', [
'src/main-executable/main.c', 'src/main-executable/main.c',
'src/main-executable/actions.c',
'src/main-executable/back_button.c', 'src/main-executable/back_button.c',
'src/main-executable/bionic_compat.c', 'src/main-executable/bionic_compat.c',
'src/main-executable/libc_bio_path_overrides.c', 'src/main-executable/libc_bio_path_overrides.c',

View File

@@ -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 /* 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 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 GHashTable *pending_notifications = NULL;
static GMutex pending_notifications_mutex = {0}; static GMutex pending_notifications_mutex = {0};
static GSource *send_notifcation_timer = NULL; static GSource *send_notifcation_timer = NULL;
static gboolean send_notifcation_func(GSource *send_notifcation_timer, GSourceFunc callback, gpointer user_data) static gboolean send_notifcation_func(GSource *send_notifcation_timer, GSourceFunc callback, gpointer user_data)
{ {
printf("Sending notifications\n");
GApplication *app = g_application_get_default(); GApplication *app = g_application_get_default();
GHashTableIter iter; GHashTableIter iter;
gpointer key, value; gpointer key, value;
@@ -58,20 +59,6 @@ static void unref_nullsafe(void *data) {
g_object_unref(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) { static void queue_notification(int id, GNotification *notification) {
g_mutex_lock(&pending_notifications_mutex); g_mutex_lock(&pending_notifications_mutex);
if (!pending_notifications) { if (!pending_notifications) {
@@ -81,22 +68,21 @@ static void queue_notification(int id, GNotification *notification) {
GApplication *app = g_application_get_default(); GApplication *app = g_application_get_default();
gchar *desktop_id = g_strdup_printf("%s.desktop", g_application_get_application_id(app)); gchar *desktop_id = g_strdup_printf("%s.desktop", g_application_get_application_id(app));
GDesktopAppInfo *info = g_desktop_app_info_new(desktop_id); 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); setenv("GNOTIFICATION_BACKEND", "freedesktop", 0);
else else
g_object_unref(info); g_object_unref(info);
g_free(desktop_id); 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); ongoing_notifications = g_hash_table_new(NULL, NULL);
} }
g_hash_table_insert(pending_notifications, GINT_TO_POINTER(id), notification); g_hash_table_insert(pending_notifications, GINT_TO_POINTER(id), notification);
g_mutex_unlock(&pending_notifications_mutex); g_mutex_unlock(&pending_notifications_mutex);
if (g_source_get_ready_time(send_notifcation_timer) == -1) { if (g_source_get_ready_time(send_notifcation_timer) == -1)
g_source_set_ready_time(send_notifcation_timer, 0); // immediately g_source_set_ready_time(send_notifcation_timer, 0); // immediately
}
} }
JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder(JNIEnv *env, jobject this) 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("")); return _INTPTR(g_notification_new(""));
} }
static GVariant *serialize_intent(JNIEnv *env, jint type, jstring action_jstr, jstring className_jstr, jstring data_jstr) JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction(JNIEnv *env, jobject this, jlong builder_ptr, jstring name_jstr, jint type, jobject intent)
{
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)
{ {
GNotification *notification = _PTR(builder_ptr); GNotification *notification = _PTR(builder_ptr);
const char *name = ""; const char *name = "";
if (name_jstr) { if (name_jstr)
name = (*env)->GetStringUTFChars(env, name_jstr, NULL); name = (*env)->GetStringUTFChars(env, name_jstr, NULL);
} g_notification_add_button_with_target_value(notification, name, intent_actionname_from_type(type), intent_serialize(env, intent));
g_notification_add_button_with_target_value(notification, name, "app.notificationaction", serialize_intent(env, type, action, className, data)); if (name_jstr)
if (name_jstr) {
(*env)->ReleaseStringUTFChars(env, name_jstr, name); (*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); GNotification *notification = _PTR(builder_ptr);
@@ -157,7 +129,7 @@ JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotificati
g_free(icon_path_full); g_free(icon_path_full);
(*env)->ReleaseStringUTFChars(env, icon_jstr, icon_path); (*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); queue_notification(id, notification);
if (ongoing) if (ongoing)
g_hash_table_add(ongoing_notifications, GINT_TO_POINTER(id)); g_hash_table_add(ongoing_notifications, GINT_TO_POINTER(id));

View File

@@ -18,18 +18,18 @@ JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder
/* /*
* Class: android_app_NotificationManager * Class: android_app_NotificationManager
* Method: nativeAddAction * 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 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 * Class: android_app_NotificationManager
* Method: nativeShowNotification * 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 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 * Class: android_app_NotificationManager

View File

@@ -159,6 +159,8 @@ void set_up_handle_cache(JNIEnv *env)
if((*env)->ExceptionCheck(env)) if((*env)->ExceptionCheck(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.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"));
handle_cache.application.get_app_icon_path = _METHOD(handle_cache.application.class, "get_app_icon_path", "()Ljava/lang/String;"); 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, "<init>", "()V"); handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "<init>", "()V");
handle_cache.intent.putExtraCharSequence = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;"); 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.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")); 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.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.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) { 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); 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;
}
}

View File

@@ -103,6 +103,8 @@ struct handle_cache {
jclass class; jclass class;
jmethodID get_package_name; jmethodID get_package_name;
jmethodID sendBroadcast; jmethodID sendBroadcast;
jmethodID startActivity;
jmethodID startService;
} context; } context;
struct { struct {
jclass class; jclass class;
@@ -128,6 +130,8 @@ struct handle_cache {
jmethodID constructor; jmethodID constructor;
jmethodID putExtraCharSequence; jmethodID putExtraCharSequence;
jmethodID putExtraByteArray; jmethodID putExtraByteArray;
jmethodID getDataString;
jmethodID setClassName;
} intent; } intent;
struct { struct {
jclass class; jclass class;
@@ -143,6 +147,10 @@ struct handle_cache {
jclass class; jclass class;
jmethodID drawText; jmethodID drawText;
} canvas; } canvas;
struct {
jclass class;
jmethodID parse;
} uri;
}; };
extern struct handle_cache handle_cache; 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_allocate(GtkWidget *widget);
void atl_safe_gtk_widget_queue_resize(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 #endif

View File

@@ -4,10 +4,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import android.app.Notification.MediaStyle; import android.app.Notification.MediaStyle;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -30,28 +28,20 @@ public class NotificationManager {
long builder = nativeInitBuilder(); long builder = nativeInitBuilder();
for (Notification.Action action : notification.actions) { for (Notification.Action action : notification.actions) {
int intentType = -1; int intentType = -1;
String actionName = null; Intent intent = null;
String className = null;
String data = null;
if (action.intent != null) { if (action.intent != null) {
intentType = action.intent.type; intentType = action.intent.type;
actionName = action.intent.intent.getAction(); intent = action.intent.intent;
className = action.intent.intent.getComponent() != null ? action.intent.intent.getComponent().getClassName() : null;
data = action.intent.intent.getData() != null ? action.intent.intent.getData().toString() : null;
} }
nativeAddAction(builder, action.title, intentType, actionName, className, data); nativeAddAction(builder, action.title, intentType, intent);
} }
int intentType = -1; int intentType = -1;
String actionName = null; Intent intent = null;
String className = null;
String data = null;
if (notification.intent != null) { if (notification.intent != null) {
intentType = notification.intent.type; intentType = notification.intent.type;
actionName = notification.intent.intent.getAction(); intent = notification.intent.intent;
className = notification.intent.intent.getComponent() != null ? notification.intent.intent.getComponent().getClassName() : null;
data = notification.intent.intent.getData() != null ? notification.intent.intent.getData().toString() : null;
} }
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) { public void notify(int id, Notification notification) {
@@ -78,29 +68,11 @@ public class NotificationManager {
cancel(null, id); 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) {} public void createNotificationChannel(NotificationChannel channel) {}
protected native long nativeInitBuilder(); protected native long nativeInitBuilder();
protected native void nativeAddAction(long builder, String title, 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, String action, String className, String data); 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 nativeShowMPRIS(String packageName, String identiy);
protected native void nativeCancel(int id); protected native void nativeCancel(int id);
protected native void nativeCancelMPRIS(); protected native void nativeCancelMPRIS();

View File

@@ -0,0 +1,49 @@
#include <gio/gio.h>
#include <jni.h>
#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);

View File

@@ -0,0 +1,4 @@
#include <gio/gio.h>
extern const GActionEntry action_entries[];
extern const int action_entries_count;

View File

@@ -12,6 +12,7 @@
#include "back_button.h" #include "back_button.h"
#include "libc_bio_path_overrides.h" #include "libc_bio_path_overrides.h"
#include "actions.h"
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
@@ -714,6 +715,19 @@ static void open(GtkApplication *app, GFile **files, gint nfiles, const gchar *h
if ((*env)->ExceptionCheck(env)) if ((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(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) 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_jvm_options = NULL;
callback_data->extra_string_keys = NULL; callback_data->extra_string_keys = NULL;
bool has_app_id = false; app = gtk_application_new("com.example.demo_application", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN | G_APPLICATION_CAN_OVERRIDE_APP_ID);
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);
// cmdline related setup // cmdline related setup
init_cmd_parameters(G_APPLICATION(app), callback_data); 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, "activate", G_CALLBACK(activate), callback_data);
g_signal_connect(app, "open", G_CALLBACK(open), 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); status = g_application_run(G_APPLICATION(app), argc, argv);
remove_ongoing_notifications(); remove_ongoing_notifications();
g_object_unref(app); g_object_unref(app);