NotificationManager: handle non player part of MPRIS

This exposes the package name to MPRIS and allows to raise the
application to the front when clicking the MPRIS notification.
This commit is contained in:
Julian Winkler
2024-07-16 22:10:44 +02:00
parent b54bed4784
commit 9d27fc138b
6 changed files with 67 additions and 12 deletions

View File

@@ -1,10 +1,16 @@
#include <gtk/gtk.h>
#include <libportal/portal.h> #include <libportal/portal.h>
#include "../defines.h" #include "../defines.h"
#include "../util.h" #include "../util.h"
#include "mpris-dbus.h"
#include "../generated_headers/android_app_NotificationManager.h" #include "../generated_headers/android_app_NotificationManager.h"
#define MPRIS_BUS_NAME_PREFIX "org.mpris.MediaPlayer2."
#define MPRIS_OBJECT_NAME "/org/mpris/MediaPlayer2"
static XdpPortal *portal = NULL; static XdpPortal *portal = NULL;
static GHashTable *ongoing_notifications = NULL; static GHashTable *ongoing_notifications = NULL;
@@ -137,3 +143,45 @@ void remove_ongoing_notifications()
if (ongoing_notifications) if (ongoing_notifications)
g_hash_table_foreach(ongoing_notifications, remove_ongoing_notification, NULL); g_hash_table_foreach(ongoing_notifications, remove_ongoing_notification, NULL);
} }
static MediaPlayer2 *mpris = NULL;
extern MediaPlayer2Player *mpris_player;
extern GtkWindow *window;
static gboolean on_media_player_handle_raise(MediaPlayer2 *mpris, GDBusMethodInvocation *invocation, gpointer user_data)
{
gtk_window_present(window);
media_player2_complete_raise(mpris, invocation);
return TRUE;
}
static void on_bus_acquired(GDBusConnection *connection, const char *name, gpointer user_data)
{
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(mpris),
connection, MPRIS_OBJECT_NAME, NULL);
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(mpris_player),
connection, MPRIS_OBJECT_NAME, NULL);
}
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowMPRIS(JNIEnv *env, jobject this, jstring package_name_jstr, jstring identity_jstr)
{
if (!mpris) {
mpris = media_player2_skeleton_new();
g_signal_connect(mpris, "handle-raise", G_CALLBACK(on_media_player_handle_raise), NULL);
g_bus_own_name(G_BUS_TYPE_SESSION, MPRIS_BUS_NAME_PREFIX "ATL", G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired, NULL, NULL, mpris, NULL);
}
media_player2_set_can_raise(mpris, TRUE);
if (package_name_jstr) {
const char *package_name = (*env)->GetStringUTFChars(env, package_name_jstr, NULL);
media_player2_set_desktop_entry(mpris, package_name);
(*env)->ReleaseStringUTFChars(env, package_name_jstr, package_name);
}
if (identity_jstr) {
const char *identity = (*env)->GetStringUTFChars(env, identity_jstr, NULL);
media_player2_set_identity(mpris, identity);
(*env)->ReleaseStringUTFChars(env, identity_jstr, identity);
}
}

View File

@@ -31,6 +31,14 @@ JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction
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); (JNIEnv *, jobject, jlong, jint, jstring, jstring, jstring, jboolean, jint, jstring, jstring);
/*
* Class: android_app_NotificationManager
* Method: nativeShowMPRIS
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowMPRIS
(JNIEnv *, jobject, jstring, jstring);
/* /*
* Class: android_app_NotificationManager * Class: android_app_NotificationManager
* Method: nativeCancel * Method: nativeCancel

View File

@@ -6,19 +6,12 @@
#include "../generated_headers/android_media_session_MediaSession.h" #include "../generated_headers/android_media_session_MediaSession.h"
#include "../generated_headers/android_os_SystemClock.h" #include "../generated_headers/android_os_SystemClock.h"
#define MPRIS_BUS_NAME_PREFIX "org.mpris.MediaPlayer2."
#define MPRIS_OBJECT_NAME "/org/mpris/MediaPlayer2" #define MPRIS_OBJECT_NAME "/org/mpris/MediaPlayer2"
MediaPlayer2Player *mpris_player = NULL; MediaPlayer2Player *mpris_player = NULL;
static jobject callback = NULL; static jobject callback = NULL;
static jlong last_position = 0; // playback_position - SystemClock.elapsedRealtime in ms static jlong last_position = 0; // playback_position - SystemClock.elapsedRealtime in ms
static void on_bus_acquired(GDBusConnection *connection, const char *name, gpointer user_data)
{
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON (mpris_player),
connection, MPRIS_OBJECT_NAME, NULL);
}
static gboolean on_media_player_handle_action(MediaPlayer2Player *mpris_player, GDBusMethodInvocation *invocation, char *method) static gboolean on_media_player_handle_action(MediaPlayer2Player *mpris_player, GDBusMethodInvocation *invocation, char *method)
{ {
if (callback) { if (callback) {
@@ -77,8 +70,6 @@ JNIEXPORT void JNICALL Java_android_media_session_MediaSession_nativeSetState(JN
"signal::handle-seek", on_media_player_handle_seek, NULL, "signal::handle-seek", on_media_player_handle_seek, NULL,
"signal::handle-set-position", on_media_player_handle_set_position, NULL, "signal::handle-set-position", on_media_player_handle_set_position, NULL,
NULL); NULL);
g_bus_own_name(G_BUS_TYPE_SESSION, MPRIS_BUS_NAME_PREFIX "ATL", G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired, NULL, NULL, mpris_player, NULL);
} }
media_player2_player_set_playback_status(mpris_player, playback_states[state < 4 ? state : 0]); media_player2_player_set_playback_status(mpris_player, playback_states[state < 4 ? state : 0]);
media_player2_player_set_position(mpris_player, position * 1000); media_player2_player_set_position(mpris_player, position * 1000);

View File

@@ -1,4 +1,10 @@
<node> <node>
<interface name="org.mpris.MediaPlayer2">
<method name="Raise"/>
<property name="CanRaise" type="b" access="read"/>
<property name="Identity" type="s" access="read"/>
<property name="DesktopEntry" type="s" access="read"/>
</interface>
<interface name="org.mpris.MediaPlayer2.Player"> <interface name="org.mpris.MediaPlayer2.Player">
<method name="Play"/> <method name="Play"/>
<method name="Pause"/> <method name="Pause"/>

View File

@@ -12,7 +12,7 @@ public class Application extends ContextWrapper {
return getString(pkg.applicationInfo.icon); return getString(pkg.applicationInfo.icon);
} }
private String get_app_label() { String get_app_label() {
return getString(pkg.applicationInfo.labelRes); return getString(pkg.applicationInfo.labelRes);
} }

View File

@@ -11,8 +11,9 @@ public class NotificationManager {
public void cancelAll() {} public void cancelAll() {}
public void notify(String tag, int id, Notification notification) { public void notify(String tag, int id, Notification notification) {
if (notification.style instanceof MediaStyle) { if (notification.style instanceof MediaStyle) { // MPRIS content is handled by MediaSession implementation
return; // MPRIS is handled by MediaSession implementation nativeShowMPRIS(Context.this_application.getPackageName(), Context.this_application.get_app_label());
return;
} }
System.out.println("notify(" + tag + ", " + id + ", " + notification + ") called"); System.out.println("notify(" + tag + ", " + id + ", " + notification + ") called");
@@ -76,5 +77,6 @@ public class NotificationManager {
protected native long nativeInitBuilder(); protected native long nativeInitBuilder();
protected native void nativeAddAction(long builder, String title, int intentType, String action, String className); protected native void nativeAddAction(long builder, String title, int intentType, String action, String className);
protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, String action, String className); protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, String action, String className);
protected native void nativeShowMPRIS(String packageName, String identiy);
protected native void nativeCancel(int id); protected native void nativeCancel(int id);
} }