Skip to content

Commit

Permalink
Merge pull request #8678 from Stypox/media-session-ui
Browse files Browse the repository at this point in the history
Create media session UI and fix player notification
  • Loading branch information
litetex authored Aug 25, 2022
2 parents ce6f3ca + 59d1ded commit 75917c7
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 455 deletions.
119 changes: 60 additions & 59 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.util.Log;
Expand Down Expand Up @@ -99,14 +98,13 @@
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
import org.schabi.newpipe.player.helper.AudioReactor;
import org.schabi.newpipe.player.helper.LoadController;
import org.schabi.newpipe.player.helper.MediaSessionManager;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.mediaitem.MediaItemTag;
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
import org.schabi.newpipe.player.notification.NotificationPlayerUi;
import org.schabi.newpipe.player.playback.MediaSourceManager;
import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.player.playback.PlayerMediaSession;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
Expand Down Expand Up @@ -176,6 +174,7 @@ public final class Player implements PlaybackListener, Listener {
//////////////////////////////////////////////////////////////////////////*/

public static final int RENDERER_UNAVAILABLE = -1;
private static final String PICASSO_PLAYER_THUMBNAIL_TAG = "PICASSO_PLAYER_THUMBNAIL_TAG";

/*//////////////////////////////////////////////////////////////////////////
// Playback
Expand All @@ -196,7 +195,6 @@ public final class Player implements PlaybackListener, Listener {

private ExoPlayer simpleExoPlayer;
private AudioReactor audioReactor;
private MediaSessionManager mediaSessionManager;

@NonNull private final DefaultTrackSelector trackSelector;
@NonNull private final LoadController loadController;
Expand Down Expand Up @@ -224,8 +222,8 @@ public final class Player implements PlaybackListener, Listener {
// UIs, listeners and disposables
//////////////////////////////////////////////////////////////////////////*/

@SuppressWarnings("MemberName") // keep the unusual member name
private final PlayerUiList UIs = new PlayerUiList();
@SuppressWarnings({"MemberName", "java:S116"}) // keep the unusual member name
private final PlayerUiList UIs;

private BroadcastReceiver broadcastReceiver;
private IntentFilter intentFilter;
Expand All @@ -235,6 +233,11 @@ public final class Player implements PlaybackListener, Listener {
@NonNull private final SerialDisposable progressUpdateDisposable = new SerialDisposable();
@NonNull private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable();

// This is the only listener we need for thumbnail loading, since there is always at most only
// one thumbnail being loaded at a time. This field is also here to maintain a strong reference,
// which would otherwise be garbage collected since Picasso holds weak references to targets.
@NonNull private final Target currentThumbnailTarget;

/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -265,6 +268,17 @@ public Player(@NonNull final PlayerService service) {

videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
audioResolver = new AudioPlaybackResolver(context, dataSource);

currentThumbnailTarget = getCurrentThumbnailTarget();

// The UIs added here should always be present. They will be initialized when the player
// reaches the initialization step. Make sure the media session ui is before the
// notification ui in the UIs list, since the notification depends on the media session in
// PlayerUi#initPlayer(), and UIs.call() guarantees UI order is preserved.
UIs = new PlayerUiList(
new MediaSessionPlayerUi(this),
new NotificationPlayerUi(this)
);
}

private VideoPlaybackResolver.QualityResolver getQualityResolver() {
Expand Down Expand Up @@ -431,11 +445,6 @@ && isPlaybackResumeEnabled(this)
}

private void initUIsForCurrentPlayerType() {
//noinspection SimplifyOptionalCallChains
if (!UIs.get(NotificationPlayerUi.class).isPresent()) {
UIs.addAndPrepare(new NotificationPlayerUi(this));
}

if ((UIs.get(MainPlayerUi.class).isPresent() && playerType == PlayerType.MAIN)
|| (UIs.get(PopupPlayerUi.class).isPresent() && playerType == PlayerType.POPUP)) {
// correct UI already in place
Expand Down Expand Up @@ -506,8 +515,6 @@ private void initPlayer(final boolean playOnReady) {
simpleExoPlayer.setHandleAudioBecomingNoisy(true);

audioReactor = new AudioReactor(context, simpleExoPlayer);
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
new PlayerMediaSession(this));

registerBroadcastReceiver();

Expand Down Expand Up @@ -558,9 +565,6 @@ private void destroyPlayer() {
if (playQueueManager != null) {
playQueueManager.dispose();
}
if (mediaSessionManager != null) {
mediaSessionManager.dispose();
}
}

public void destroy() {
Expand All @@ -577,7 +581,7 @@ public void destroy() {

databaseUpdateDisposable.clear();
progressUpdateDisposable.set(null);
PicassoHelper.cancelTag(PicassoHelper.PLAYER_THUMBNAIL_TAG); // cancel thumbnail loading
cancelLoadingCurrentThumbnail();

UIs.destroyAll(Object.class); // destroy every UI: obviously every UI extends Object
}
Expand Down Expand Up @@ -723,11 +727,6 @@ private void onBroadcastReceived(final Intent intent) {
Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received");
}
break;
case Intent.ACTION_HEADSET_PLUG: //FIXME
/*notificationManager.cancel(NOTIFICATION_ID);
mediaSessionManager.dispose();
mediaSessionManager.enable(getBaseContext(), basePlayerImpl.simpleExoPlayer);*/
break;
}

UIs.call(playerUi -> playerUi.onBroadcastReceived(intent));
Expand Down Expand Up @@ -756,44 +755,63 @@ private void unregisterBroadcastReceiver() {
//////////////////////////////////////////////////////////////////////////*/
//region Thumbnail loading

private void initThumbnail(final String url) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - initThumbnail() called with url = ["
+ (url == null ? "null" : url) + "]");
}
if (isNullOrEmpty(url)) {
return;
}

// scale down the notification thumbnail for performance
PicassoHelper.loadScaledDownThumbnail(context, url).into(new Target() {
private Target getCurrentThumbnailTarget() {
// a Picasso target is just a listener for thumbnail loading events
return new Target() {
@Override
public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - onLoadingComplete() called with: url = [" + url
+ "], " + "loadedImage = [" + bitmap + " -> " + bitmap.getWidth() + "x"
+ bitmap.getHeight() + "], from = [" + from + "]");
Log.d(TAG, "Thumbnail - onBitmapLoaded() called with: bitmap = [" + bitmap
+ " -> " + bitmap.getWidth() + "x" + bitmap.getHeight() + "], from = ["
+ from + "]");
}

currentThumbnail = bitmap;
// there is a new thumbnail, so changed the end screen thumbnail, too.
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
UIs.call(playerUi -> playerUi.onThumbnailLoaded(bitmap));
}

@Override
public void onBitmapFailed(final Exception e, final Drawable errorDrawable) {
Log.e(TAG, "Thumbnail - onBitmapFailed() called: url = [" + url + "]", e);
Log.e(TAG, "Thumbnail - onBitmapFailed() called", e);
currentThumbnail = null;
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
UIs.call(playerUi -> playerUi.onThumbnailLoaded(null));
}

@Override
public void onPrepareLoad(final Drawable placeHolderDrawable) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - onPrepareLoad() called: url = [" + url + "]");
Log.d(TAG, "Thumbnail - onPrepareLoad() called");
}
}
});
};
}

private void loadCurrentThumbnail(final String url) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with url = ["
+ (url == null ? "null" : url) + "]");
}

// first cancel any previous loading
cancelLoadingCurrentThumbnail();

// Unset currentThumbnail, since it is now outdated. This ensures it is not used in media
// session metadata while the new thumbnail is being loaded by Picasso.
currentThumbnail = null;
if (isNullOrEmpty(url)) {
return;
}

// scale down the notification thumbnail for performance
PicassoHelper.loadScaledDownThumbnail(context, url)
.tag(PICASSO_PLAYER_THUMBNAIL_TAG)
.into(currentThumbnailTarget);
}

private void cancelLoadingCurrentThumbnail() {
// cancel the Picasso job associated with the player thumbnail, if any
PicassoHelper.cancelTag(PICASSO_PLAYER_THUMBNAIL_TAG);
}
//endregion

Expand Down Expand Up @@ -1735,18 +1753,9 @@ private void updateMetadataWith(@NonNull final StreamInfo info) {

maybeAutoQueueNextStream(info);

initThumbnail(info.getThumbnailUrl());
loadCurrentThumbnail(info.getThumbnailUrl());
registerStreamViewed();

final boolean showThumbnail = prefs.getBoolean(
context.getString(R.string.show_thumbnail_key), true);
mediaSessionManager.setMetadata(
getVideoTitle(),
getUploaderName(),
showThumbnail ? Optional.ofNullable(getThumbnail()) : Optional.empty(),
StreamTypeUtil.isLiveStream(info.getStreamType()) ? -1 : info.getDuration()
);

notifyMetadataUpdateToListeners();
UIs.call(playerUi -> playerUi.onMetadataChanged(info));
}
Expand Down Expand Up @@ -1786,10 +1795,6 @@ public String getUploaderName() {

@Nullable
public Bitmap getThumbnail() {
if (currentThumbnail == null) {
currentThumbnail = BitmapFactory.decodeResource(
context.getResources(), R.drawable.placeholder_thumbnail_video);
}
return currentThumbnail;
}
//endregion
Expand Down Expand Up @@ -2194,10 +2199,6 @@ public SharedPreferences getPrefs() {
return prefs;
}

public MediaSessionManager getMediaSessionManager() {
return mediaSessionManager;
}


public PlayerType getPlayerType() {
return playerType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import android.os.IBinder;
import android.util.Log;

import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
import org.schabi.newpipe.util.ThemeHelper;


Expand Down Expand Up @@ -73,9 +74,8 @@ public int onStartCommand(final Intent intent, final int flags, final int startI
}

player.handleIntent(intent);
if (player.getMediaSessionManager() != null) {
player.getMediaSessionManager().handleMediaButtonIntent(intent);
}
player.UIs().get(MediaSessionPlayerUi.class)
.ifPresent(ui -> ui.handleMediaButtonIntent(intent));

return START_NOT_STICKY;
}
Expand Down
Loading

0 comments on commit 75917c7

Please sign in to comment.