From 499106b67abf045f42b16c24cd21b222f54a5562 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Thu, 28 Dec 2023 22:46:59 -0600 Subject: [PATCH 1/5] android: add next and previous track to the jni bridge This will allow the android client to directly make calls to the mpd process to change tracks I went with camel case on the function names here, if you use an underscore javac generates a function tht looks like this: JNIEXPORT void JNICALL Java_org_musicpd_Bridge_play_1previous I figured what we ended up with looks a little nicer: JNIEXPORT void JNICALL Java_org_musicpd_Bridge_playPrevious --- android/app/src/main/java/org/musicpd/Bridge.java | 2 ++ src/Main.cxx | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/android/app/src/main/java/org/musicpd/Bridge.java b/android/app/src/main/java/org/musicpd/Bridge.java index 6ad49e2d4a..0ca31ebe6b 100644 --- a/android/app/src/main/java/org/musicpd/Bridge.java +++ b/android/app/src/main/java/org/musicpd/Bridge.java @@ -18,4 +18,6 @@ public interface LogListener { public static native void run(Context context, LogListener logListener); public static native void shutdown(); public static native void pause(); + public static native void playNext(); + public static native void playPrevious(); } diff --git a/src/Main.cxx b/src/Main.cxx index 4a469bc4f7..20ca50ce4c 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -614,6 +614,20 @@ Java_org_musicpd_Bridge_pause(JNIEnv *, jclass) partition.pc.LockSetPause(true); } +gcc_visibility_default +JNIEXPORT void JNICALL +Java_org_musicpd_Bridge_playNext(JNIEnv *, jclass) +{ + +} + +gcc_visibility_default +JNIEXPORT void JNICALL +Java_org_musicpd_Bridge_playPrevious(JNIEnv *, jclass) +{ + +} + #else static inline void From 568bb7d5360464871d34c158779cf96de4c7d323 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Mon, 1 Jan 2024 20:51:07 -0600 Subject: [PATCH 2/5] android: jni function definitions to get the current song --- android/app/src/main/java/org/musicpd/Bridge.java | 1 + src/Main.cxx | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/android/app/src/main/java/org/musicpd/Bridge.java b/android/app/src/main/java/org/musicpd/Bridge.java index 0ca31ebe6b..d547a5d4f3 100644 --- a/android/app/src/main/java/org/musicpd/Bridge.java +++ b/android/app/src/main/java/org/musicpd/Bridge.java @@ -20,4 +20,5 @@ public interface LogListener { public static native void pause(); public static native void playNext(); public static native void playPrevious(); + public static native Object currentSong(); } diff --git a/src/Main.cxx b/src/Main.cxx index 20ca50ce4c..34c5186838 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -628,6 +628,13 @@ Java_org_musicpd_Bridge_playPrevious(JNIEnv *, jclass) } +gcc_visibility_default +JNIEXPORT jobject JNICALL +Java_org_musicpd_Bridge_currentSong(JNIEnv *env, jclass) +{ + return NULL; +} + #else static inline void From 4183734c81360d42f74296af45d7e4a7edadc8eb Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Mon, 1 Jan 2024 20:56:24 -0600 Subject: [PATCH 3/5] android: Add new SongInfo class to the jni bridge This class will allow us to send up the equivalent of print_song_info to the bridge class. --- android/app/src/main/java/org/musicpd/Bridge.java | 4 +++- .../app/src/main/java/org/musicpd/models/SongInfo.java | 9 +++++++++ android/include/meson.build | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/java/org/musicpd/models/SongInfo.java diff --git a/android/app/src/main/java/org/musicpd/Bridge.java b/android/app/src/main/java/org/musicpd/Bridge.java index d547a5d4f3..a67175d1c1 100644 --- a/android/app/src/main/java/org/musicpd/Bridge.java +++ b/android/app/src/main/java/org/musicpd/Bridge.java @@ -5,6 +5,8 @@ import android.content.Context; +import org.musicpd.models.SongInfo; + /** * Bridge to native code. */ @@ -20,5 +22,5 @@ public interface LogListener { public static native void pause(); public static native void playNext(); public static native void playPrevious(); - public static native Object currentSong(); + public static native SongInfo currentSong(); } diff --git a/android/app/src/main/java/org/musicpd/models/SongInfo.java b/android/app/src/main/java/org/musicpd/models/SongInfo.java new file mode 100644 index 0000000000..dd9a037e3b --- /dev/null +++ b/android/app/src/main/java/org/musicpd/models/SongInfo.java @@ -0,0 +1,9 @@ +package org.musicpd.models; + +import java.util.HashMap; + +public class SongInfo { + String uri; + int durationMilliseconds; + HashMap tags; +} diff --git a/android/include/meson.build b/android/include/meson.build index af9f57ebf4..cce8b5394c 100644 --- a/android/include/meson.build +++ b/android/include/meson.build @@ -5,6 +5,7 @@ bridge_header = custom_target( output: 'org_musicpd_Bridge.h', input: [ '../app/src/main/java/org/musicpd/Bridge.java', + '../app/src/main/java/org/musicpd/models/SongInfo.java', ], command: [ javac, From f61d36c93d020e48f0dbb64ffbef18d328944a7e Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Mon, 1 Jan 2024 21:24:21 -0600 Subject: [PATCH 4/5] android: Add functions to convert DetachedSong to Java SongInfo object This moves all the song tags into the hashmap and moves that into the song info object along with duration and the song uri. It makes sure to clean up local references except for the values passed through in return. In the future we could prefetch or cache the jni field IDs to get a bit of a performance improvement. --- meson.build | 1 + src/android/Song.cxx | 46 ++++++++++++++++++++++++++++++++++++++++++++ src/android/Song.hxx | 7 +++++++ 3 files changed, 54 insertions(+) create mode 100644 src/android/Song.cxx create mode 100644 src/android/Song.hxx diff --git a/meson.build b/meson.build index d56f64505e..8240e462b4 100644 --- a/meson.build +++ b/meson.build @@ -438,6 +438,7 @@ else 'src/android/AudioManager.cxx', 'src/android/Environment.cxx', 'src/android/LogListener.cxx', + 'src/android/Song.cxx', ] endif diff --git a/src/android/Song.cxx b/src/android/Song.cxx new file mode 100644 index 0000000000..c8205e0947 --- /dev/null +++ b/src/android/Song.cxx @@ -0,0 +1,46 @@ +#include "Song.hxx" +#include "java/Class.hxx" +#include "tag/Names.hxx" + +jobject song_to_song_info(JNIEnv *env, std::unique_ptr &song) { + jobject tag_map = song_to_tag_hashmap(env, song); + + Java::Class cls(env, "org/musicpd/models/SongInfo"); + jmethodID init = env->GetMethodID(cls, "", "()V"); + jobject song_info = env->NewObject(cls, init); + + jstring uri = env->NewStringUTF(song->GetURI()); + jfieldID id_uri = env->GetFieldID(cls, "uri", "Ljava/lang/String;"); + env->SetObjectField(song_info, id_uri, uri); + env->DeleteLocalRef(uri); + + const auto duration = song->GetDuration(); + jfieldID id_duration = env->GetFieldID(cls, "durationMilliseconds", "I"); + env->SetIntField(song_info, id_duration, duration.ToMS()); + + jfieldID id_tags = env->GetFieldID(cls, "tags", "Ljava/util/HashMap;"); + env->SetObjectField(song_info, id_tags, tag_map); + env->DeleteLocalRef(tag_map); + + return song_info; +} + +jobject song_to_tag_hashmap(JNIEnv *env, std::unique_ptr &song) { + Java::Class cls(env, "java/util/HashMap"); + jmethodID init = env->GetMethodID(cls, "", "()V"); + jobject hash_map = env->NewObject(cls, init); + jmethodID put = env->GetMethodID(cls, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + const Tag &tag = song->GetTag(); + for (const auto &i : tag) { + jstring key = env->NewStringUTF(tag_item_names[i.type]); + jstring value = env->NewStringUTF(i.value); + + env->CallObjectMethod(hash_map, put, key, value); + + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } + + return hash_map; +} \ No newline at end of file diff --git a/src/android/Song.hxx b/src/android/Song.hxx new file mode 100644 index 0000000000..3946e2dda8 --- /dev/null +++ b/src/android/Song.hxx @@ -0,0 +1,7 @@ +#include "song/DetachedSong.hxx" + +#include "java/Object.hxx" + +jobject song_to_song_info(JNIEnv *env, std::unique_ptr &song); + +jobject song_to_tag_hashmap(JNIEnv *env, std::unique_ptr &song); \ No newline at end of file From d85dd30d61e76dd7f60b3bda9dc5a2a9a7c850c9 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Mon, 1 Jan 2024 21:30:02 -0600 Subject: [PATCH 5/5] android: build and return the current song over the jni bridge --- src/Main.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Main.cxx b/src/Main.cxx index 34c5186838..fdf3a97329 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -79,6 +79,7 @@ #include "android/Environment.hxx" #include "android/Context.hxx" #include "android/LogListener.hxx" +#include "android/Song.hxx" #include "config/File.hxx" #include "fs/FileSystem.hxx" #include "org_musicpd_Bridge.h" @@ -632,6 +633,17 @@ gcc_visibility_default JNIEXPORT jobject JNICALL Java_org_musicpd_Bridge_currentSong(JNIEnv *env, jclass) { + if (global_instance != nullptr) { + for (auto &partition : global_instance->partitions) { + int current_position = partition.playlist.GetCurrentPosition(); + if (current_position < 0) + continue; + + std::unique_ptr song = std::make_unique(partition.playlist.queue.Get(current_position)); + return song_to_song_info(env, song); + } + } + return NULL; }