You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
implementing MediaSession using MPRIS
NotificationManager will now ignore MediaStyle notifications
This commit is contained in:
117
src/api-impl-jni/media/android_media_session_MediaSession.c
Normal file
117
src/api-impl-jni/media/android_media_session_MediaSession.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "../util.h"
|
||||
#include "../defines.h"
|
||||
|
||||
#include "mpris-dbus.h"
|
||||
|
||||
#include "../generated_headers/android_media_session_MediaSession.h"
|
||||
#include "../generated_headers/android_os_SystemClock.h"
|
||||
|
||||
#define MPRIS_BUS_NAME_PREFIX "org.mpris.MediaPlayer2."
|
||||
#define MPRIS_OBJECT_NAME "/org/mpris/MediaPlayer2"
|
||||
|
||||
MediaPlayer2Player *mpris_player = NULL;
|
||||
static jobject callback = NULL;
|
||||
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)
|
||||
{
|
||||
if (callback) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
(*env)->CallVoidMethod(env, callback, _METHOD(_CLASS(callback), method, "()V"));
|
||||
}
|
||||
g_dbus_method_invocation_return_value(invocation, g_variant_new("()"));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean on_media_player_handle_play_pause(MediaPlayer2Player *mpris_player, GDBusMethodInvocation *invocation, gpointer user_data)
|
||||
{
|
||||
gboolean is_playing = !strcmp("Playing", media_player2_player_get_playback_status(mpris_player));
|
||||
return on_media_player_handle_action(mpris_player, invocation, is_playing ? "onPause" : "onPlay");
|
||||
}
|
||||
|
||||
static gboolean on_media_player_handle_seek(MediaPlayer2Player *mpris_player, GDBusMethodInvocation *invocation, gint64 offset_us, gpointer user_data)
|
||||
{
|
||||
if (callback) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
last_position += offset_us / 1000;
|
||||
(*env)->CallVoidMethod(env, callback, _METHOD(_CLASS(callback), "onSeekTo", "(J)V"), last_position + Java_android_os_SystemClock_elapsedRealtime(env, NULL));
|
||||
}
|
||||
media_player2_player_complete_seek(mpris_player, invocation);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean on_media_player_handle_set_position(MediaPlayer2Player *mpris_player, GDBusMethodInvocation *invocation, GVariant *trackid, gint64 pos_us, gpointer user_data)
|
||||
{
|
||||
if (callback) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
(*env)->CallVoidMethod(env, callback, _METHOD(_CLASS(callback), "onSeekTo", "(J)V"), pos_us / 1000);
|
||||
}
|
||||
media_player2_player_complete_set_position(mpris_player, invocation);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define ACTION_PAUSE (1 << 1)
|
||||
#define ACTION_PLAY (1 << 2)
|
||||
#define ACTION_SKIP_TO_PREVIOUS (1 << 4)
|
||||
#define ACTION_SKIP_TO_NEXT (1 << 5)
|
||||
#define ACTION_SEEK_TO (1 << 8)
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_media_session_MediaSession_nativeSetState(JNIEnv *env, jobject this, jint state,
|
||||
jlong actions, jlong position, jlong update_time, jstring title_str, jstring artist_str, jstring art_url_str)
|
||||
{
|
||||
const char *playback_states[] = {"None", "Stopped", "Paused", "Playing"};
|
||||
if (!mpris_player) {
|
||||
mpris_player = media_player2_player_skeleton_new();
|
||||
g_object_connect(mpris_player,
|
||||
"signal::handle-play", on_media_player_handle_action, "onPlay",
|
||||
"signal::handle-pause", on_media_player_handle_action, "onPause",
|
||||
"signal::handle-next", on_media_player_handle_action, "onSkipToNext",
|
||||
"signal::handle-previous", on_media_player_handle_action, "onSkipToPrevious",
|
||||
"signal::handle-play-pause", on_media_player_handle_play_pause, NULL,
|
||||
"signal::handle-seek", on_media_player_handle_seek, NULL,
|
||||
"signal::handle-set-position", on_media_player_handle_set_position, 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_position(mpris_player, position * 1000);
|
||||
last_position = position - update_time;
|
||||
media_player2_player_set_can_control(mpris_player, !!(actions));
|
||||
media_player2_player_set_can_play(mpris_player, !!(actions & ACTION_PLAY));
|
||||
media_player2_player_set_can_pause(mpris_player, !!(actions & ACTION_PAUSE));
|
||||
media_player2_player_set_can_seek(mpris_player, !!(actions & ACTION_SEEK_TO));
|
||||
media_player2_player_set_can_go_next(mpris_player, !!(actions & ACTION_SKIP_TO_NEXT));
|
||||
media_player2_player_set_can_go_previous(mpris_player, !!(actions & ACTION_SKIP_TO_PREVIOUS));
|
||||
|
||||
GVariantDict dict;
|
||||
g_variant_dict_init(&dict, NULL);
|
||||
g_variant_dict_insert(&dict, "mpris:trackid", "s", MPRIS_OBJECT_NAME "/Track/0");
|
||||
if (art_url_str) {
|
||||
const char *art_url = (*env)->GetStringUTFChars(env, art_url_str, NULL);
|
||||
g_variant_dict_insert(&dict, "mpris:artUrl", "s", art_url);
|
||||
(*env)->ReleaseStringUTFChars(env, art_url_str, art_url);
|
||||
}
|
||||
if (title_str) {
|
||||
const char *title = (*env)->GetStringUTFChars(env, title_str, NULL);
|
||||
g_variant_dict_insert(&dict, "xesam:title", "s", title);
|
||||
(*env)->ReleaseStringUTFChars(env, title_str, title);
|
||||
}
|
||||
if (artist_str) {
|
||||
const char *artist = (*env)->GetStringUTFChars(env, artist_str, NULL);
|
||||
g_variant_dict_insert(&dict, "xesam:artist", "s", artist);
|
||||
(*env)->ReleaseStringUTFChars(env, artist_str, artist);
|
||||
}
|
||||
media_player2_player_set_metadata(mpris_player, g_variant_dict_end(&dict));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_media_session_MediaSession_nativeSetCallback(JNIEnv *env, jobject this, jobject new_callback)
|
||||
{
|
||||
callback = _REF(new_callback);
|
||||
}
|
||||
25
src/api-impl-jni/media/org.mpris.MediaPlayer2.xml
Normal file
25
src/api-impl-jni/media/org.mpris.MediaPlayer2.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<node>
|
||||
<interface name="org.mpris.MediaPlayer2.Player">
|
||||
<method name="Play"/>
|
||||
<method name="Pause"/>
|
||||
<method name="PlayPause"/>
|
||||
<method name="Next"/>
|
||||
<method name="Previous"/>
|
||||
<method name="Seek">
|
||||
<arg name="Offset" direction="in" type="x"/>
|
||||
</method>
|
||||
<method name="SetPosition">
|
||||
<arg name="TrackId" direction="in" type="o"/>
|
||||
<arg name="Position" direction="in" type="x"/>
|
||||
</method>
|
||||
<property name="CanControl" type="b" access="read"/>
|
||||
<property name="CanPlay" type="b" access="read"/>
|
||||
<property name="CanPause" type="b" access="read"/>
|
||||
<property name="CanSeek" type="b" access="read"/>
|
||||
<property name="CanGoNext" type="b" access="read"/>
|
||||
<property name="CanGoPrevious" type="b" access="read"/>
|
||||
<property name="Metadata" type="a{sv}" access="read"/>
|
||||
<property name="PlaybackStatus" type="s" access="read"/>
|
||||
<property name="Position" type="x" access="read"/>
|
||||
</interface>
|
||||
</node>
|
||||
Reference in New Issue
Block a user