Merge MediaSessionManager into MediaSessionPlayerUi

This commit is contained in:
Stypox 2022-07-22 12:00:32 +02:00 committed by litetex
parent f80d1dc48d
commit 11bd2369e5
2 changed files with 90 additions and 124 deletions

View file

@ -1,110 +0,0 @@
package org.schabi.newpipe.player.mediasession;
import android.content.Context;
import android.content.Intent;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.media.session.MediaButtonReceiver;
import com.google.android.exoplayer2.ForwardingPlayer;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.ui.VideoPlayerUi;
import org.schabi.newpipe.util.StreamTypeUtil;
import java.util.Optional;
public class MediaSessionManager {
private static final String TAG = MediaSessionManager.class.getSimpleName();
public static final boolean DEBUG = MainActivity.DEBUG;
@NonNull
private final MediaSessionCompat mediaSession;
@NonNull
private final MediaSessionConnector sessionConnector;
public MediaSessionManager(@NonNull final Context context,
@NonNull final Player player) {
mediaSession = new MediaSessionCompat(context, TAG);
mediaSession.setActive(true);
sessionConnector = new MediaSessionConnector(mediaSession);
sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, player));
sessionConnector.setPlayer(new ForwardingPlayer(player.getExoPlayer()) {
@Override
public void play() {
player.play();
// hide the player controls even if the play command came from the media session
player.UIs().get(VideoPlayerUi.class).ifPresent(ui -> ui.hideControls(0, 0));
}
@Override
public void pause() {
player.pause();
}
});
sessionConnector.setMetadataDeduplicationEnabled(true);
sessionConnector.setMediaMetadataProvider(exoPlayer -> {
if (DEBUG) {
Log.d(TAG, "MediaMetadataProvider#getMetadata called");
}
// set title and artist
final MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, player.getVideoTitle())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, player.getUploaderName());
// set duration (-1 for livestreams, since they don't have a duration)
final long duration = player.getCurrentStreamInfo()
.filter(info -> !StreamTypeUtil.isLiveStream(info.getStreamType()))
.map(info -> info.getDuration() * 1000L)
.orElse(-1L);
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
// set album art, unless the user asked not to, or there is no thumbnail available
final boolean showThumbnail = player.getPrefs().getBoolean(
context.getString(R.string.show_thumbnail_key), true);
Optional.ofNullable(player.getThumbnail())
.filter(bitmap -> showThumbnail)
.ifPresent(bitmap -> {
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap);
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, bitmap);
});
return builder.build();
});
}
@Nullable
@SuppressWarnings("UnusedReturnValue")
public KeyEvent handleMediaButtonIntent(final Intent intent) {
return MediaButtonReceiver.handleIntent(mediaSession, intent);
}
public MediaSessionCompat.Token getSessionToken() {
return mediaSession.getSessionToken();
}
void triggerMetadataUpdate() {
sessionConnector.invalidateMediaSessionMetadata();
}
/**
* Should be called on player destruction to prevent leakage.
*/
public void dispose() {
sessionConnector.setPlayer(null);
sessionConnector.setQueueNavigator(null);
mediaSession.setActive(false);
mediaSession.release();
}
}

View file

@ -1,20 +1,33 @@
package org.schabi.newpipe.player.mediasession; package org.schabi.newpipe.player.mediasession;
import static org.schabi.newpipe.MainActivity.DEBUG;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media.session.MediaButtonReceiver;
import com.google.android.exoplayer2.ForwardingPlayer;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import org.schabi.newpipe.R;
import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.ui.PlayerUi; import org.schabi.newpipe.player.ui.PlayerUi;
import org.schabi.newpipe.player.ui.VideoPlayerUi;
import org.schabi.newpipe.util.StreamTypeUtil;
import java.util.Optional; import java.util.Optional;
public class MediaSessionPlayerUi extends PlayerUi { public class MediaSessionPlayerUi extends PlayerUi {
private static final String TAG = "MediaSessUi";
private MediaSessionManager mediaSessionManager; private MediaSessionCompat mediaSession;
private MediaSessionConnector sessionConnector;
public MediaSessionPlayerUi(@NonNull final Player player) { public MediaSessionPlayerUi(@NonNull final Player player) {
super(player); super(player);
@ -23,18 +36,31 @@ public class MediaSessionPlayerUi extends PlayerUi {
@Override @Override
public void initPlayer() { public void initPlayer() {
super.initPlayer(); super.initPlayer();
if (mediaSessionManager != null) { destroyPlayer(); // release previously used resources
mediaSessionManager.dispose();
} mediaSession = new MediaSessionCompat(context, TAG);
mediaSessionManager = new MediaSessionManager(context, player); mediaSession.setActive(true);
sessionConnector = new MediaSessionConnector(mediaSession);
sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, player));
sessionConnector.setPlayer(getForwardingPlayer());
sessionConnector.setMetadataDeduplicationEnabled(true);
sessionConnector.setMediaMetadataProvider(exoPlayer -> buildMediaMetadata());
} }
@Override @Override
public void destroyPlayer() { public void destroyPlayer() {
super.destroyPlayer(); super.destroyPlayer();
if (mediaSessionManager != null) { if (sessionConnector != null) {
mediaSessionManager.dispose(); sessionConnector.setPlayer(null);
mediaSessionManager = null; sessionConnector.setQueueNavigator(null);
sessionConnector = null;
}
if (mediaSession != null) {
mediaSession.setActive(false);
mediaSession.release();
mediaSession = null;
} }
} }
@ -47,18 +73,68 @@ public class MediaSessionPlayerUi extends PlayerUi {
@Override @Override
public void onThumbnailLoaded(@Nullable final Bitmap bitmap) { public void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
super.onThumbnailLoaded(bitmap); super.onThumbnailLoaded(bitmap);
if (mediaSessionManager != null) { if (sessionConnector != null) {
mediaSessionManager.triggerMetadataUpdate(); // the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
sessionConnector.invalidateMediaSessionMetadata();
} }
} }
public void handleMediaButtonIntent(final Intent intent) { public void handleMediaButtonIntent(final Intent intent) {
if (mediaSessionManager != null) { MediaButtonReceiver.handleIntent(mediaSession, intent);
mediaSessionManager.handleMediaButtonIntent(intent);
}
} }
public Optional<MediaSessionCompat.Token> getSessionToken() { public Optional<MediaSessionCompat.Token> getSessionToken() {
return Optional.ofNullable(mediaSessionManager).map(MediaSessionManager::getSessionToken); return Optional.ofNullable(mediaSession).map(MediaSessionCompat::getSessionToken);
}
private ForwardingPlayer getForwardingPlayer() {
// ForwardingPlayer means that all media session actions called on this player are
// forwarded directly to the connected exoplayer, except for the overridden methods. So
// override play and pause since our player adds more functionality to them over exoplayer.
return new ForwardingPlayer(player.getExoPlayer()) {
@Override
public void play() {
player.play();
// hide the player controls even if the play command came from the media session
player.UIs().get(VideoPlayerUi.class).ifPresent(ui -> ui.hideControls(0, 0));
}
@Override
public void pause() {
player.pause();
}
};
}
private MediaMetadataCompat buildMediaMetadata() {
if (DEBUG) {
Log.d(TAG, "buildMediaMetadata called");
}
// set title and artist
final MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, player.getVideoTitle())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, player.getUploaderName());
// set duration (-1 for livestreams or if unknown, see the METADATA_KEY_DURATION docs)
final long duration = player.getCurrentStreamInfo()
.filter(info -> !StreamTypeUtil.isLiveStream(info.getStreamType()))
.map(info -> info.getDuration() * 1000L)
.orElse(-1L);
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
// set album art, unless the user asked not to, or there is no thumbnail available
final boolean showThumbnail = player.getPrefs().getBoolean(
context.getString(R.string.show_thumbnail_key), true);
Optional.ofNullable(player.getThumbnail())
.filter(bitmap -> showThumbnail)
.ifPresent(bitmap -> {
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap);
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, bitmap);
});
return builder.build();
} }
} }