Merge branch 'mediasession' of https://github.com/DDRBoxman/MPD

This commit is contained in:
Max Kellermann 2024-01-13 22:26:34 +01:00
commit 3e862b85d4
5 changed files with 104 additions and 0 deletions

View File

@ -70,6 +70,8 @@ dependencies {
ksp("com.google.dagger:dagger-compiler:2.49") ksp("com.google.dagger:dagger-compiler:2.49")
ksp("com.google.dagger:hilt-compiler:2.49") ksp("com.google.dagger:hilt-compiler:2.49")
implementation("androidx.media3:media3-session:1.2.0")
// Android Studio Preview support // Android Studio Preview support
implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")

View File

@ -18,4 +18,6 @@ public class Bridge {
public static native void run(Context context, LogListener logListener); public static native void run(Context context, LogListener logListener);
public static native void shutdown(); public static native void shutdown();
public static native void pause(); public static native void pause();
public static native void playNext();
public static native void playPrevious();
} }

View File

@ -0,0 +1,65 @@
package org.musicpd;
import android.annotation.SuppressLint;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.media3.common.Player;
import androidx.media3.common.SimpleBasePlayer;
import androidx.media3.common.util.UnstableApi;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Arrays;
import java.util.List;
@UnstableApi
public class MPDPlayer extends SimpleBasePlayer {
List<MediaItemData> placeholderItems;
public MPDPlayer(Looper looper) {
super(looper);
// Dummy items to let us receive next and previous commands
MediaItemData item0 = new MediaItemData.Builder(0)
.build();
MediaItemData item1 = new MediaItemData.Builder(1)
.build();
MediaItemData item2 = new MediaItemData.Builder(2)
.build();
MediaItemData[] items = new MediaItemData[] { item0, item1, item2 };
placeholderItems = Arrays.asList(items);
}
@NonNull
@Override
protected State getState() {
Commands commands = new Commands.Builder().addAll(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
return new State.Builder()
.setAvailableCommands(commands)
.setPlaybackState(Player.STATE_READY)
.setPlaylist(placeholderItems)
.setCurrentMediaItemIndex(1)
.build();
}
@NonNull
@SuppressLint("SwitchIntDef")
@Override
protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, int seekCommand) {
switch (seekCommand) {
case COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM:
case COMMAND_SEEK_TO_PREVIOUS:
Bridge.playPrevious();
break;
case COMMAND_SEEK_TO_NEXT_MEDIA_ITEM:
case COMMAND_SEEK_TO_NEXT:
Bridge.playNext();
break;
}
return Futures.immediateVoidFuture();
}
}

View File

@ -16,11 +16,16 @@ import android.content.ServiceConnection;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteCallbackList; import android.os.RemoteCallbackList;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaSession;
import org.musicpd.data.LoggingRepository; import org.musicpd.data.LoggingRepository;
import org.musicpd.ui.SettingsActivity; import org.musicpd.ui.SettingsActivity;
@ -51,6 +56,8 @@ public class Main extends Service implements Runnable {
private boolean mPauseOnHeadphonesDisconnect = false; private boolean mPauseOnHeadphonesDisconnect = false;
private PowerManager.WakeLock mWakelock = null; private PowerManager.WakeLock mWakelock = null;
private MediaSession mMediaSession = null;
@Inject @Inject
LoggingRepository logging; LoggingRepository logging;
@ -184,6 +191,7 @@ public class Main extends Service implements Runnable {
} }
} }
@OptIn(markerClass = UnstableApi.class)
private void start() { private void start() {
if (mThread != null) if (mThread != null)
return; return;
@ -225,11 +233,16 @@ public class Main extends Service implements Runnable {
mThread = new Thread(this); mThread = new Thread(this);
mThread.start(); mThread.start();
MPDPlayer player = new MPDPlayer(Looper.getMainLooper());
mMediaSession = new MediaSession.Builder(this, player).build();
startForeground(R.string.notification_title_mpd_running, notification); startForeground(R.string.notification_title_mpd_running, notification);
startService(new Intent(this, Main.class)); startService(new Intent(this, Main.class));
} }
private void stop() { private void stop() {
mMediaSession.release();
mMediaSession = null;
if (mThread != null) { if (mThread != null) {
if (mThread.isAlive()) { if (mThread.isAlive()) {
synchronized (this) { synchronized (this) {

View File

@ -614,6 +614,28 @@ Java_org_musicpd_Bridge_pause(JNIEnv *, jclass)
partition.pc.LockSetPause(true); partition.pc.LockSetPause(true);
} }
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_playNext(JNIEnv *, jclass)
{
if (global_instance != nullptr)
BlockingCall(global_instance->event_loop, [&](){
for (auto &partition : global_instance->partitions)
partition.PlayNext();
});
}
gcc_visibility_default
JNIEXPORT void JNICALL
Java_org_musicpd_Bridge_playPrevious(JNIEnv *, jclass)
{
if (global_instance != nullptr)
BlockingCall(global_instance->event_loop, [&](){
for (auto &partition : global_instance->partitions)
partition.PlayPrevious();
});
}
#else #else
static inline void static inline void