Skip to content

Commit ca29f6c

Browse files
authored
Merge pull request #8899 from Stypox/fix-player-thumbnail-handling
Fix wrong thumbnail in notification on Android 13
2 parents e6391a8 + ed87465 commit ca29f6c

File tree

4 files changed

+73
-54
lines changed

4 files changed

+73
-54
lines changed

app/src/main/java/org/schabi/newpipe/player/Player.java

+47-37
Original file line numberDiff line numberDiff line change
@@ -765,17 +765,15 @@ public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
765765
+ " -> " + bitmap.getWidth() + "x" + bitmap.getHeight() + "], from = ["
766766
+ from + "]");
767767
}
768-
currentThumbnail = bitmap;
769768
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
770-
UIs.call(playerUi -> playerUi.onThumbnailLoaded(bitmap));
769+
onThumbnailLoaded(bitmap);
771770
}
772771

773772
@Override
774773
public void onBitmapFailed(final Exception e, final Drawable errorDrawable) {
775774
Log.e(TAG, "Thumbnail - onBitmapFailed() called", e);
776-
currentThumbnail = null;
777775
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
778-
UIs.call(playerUi -> playerUi.onThumbnailLoaded(null));
776+
onThumbnailLoaded(null);
779777
}
780778

781779
@Override
@@ -798,7 +796,7 @@ private void loadCurrentThumbnail(final String url) {
798796

799797
// Unset currentThumbnail, since it is now outdated. This ensures it is not used in media
800798
// session metadata while the new thumbnail is being loaded by Picasso.
801-
currentThumbnail = null;
799+
onThumbnailLoaded(null);
802800
if (isNullOrEmpty(url)) {
803801
return;
804802
}
@@ -813,6 +811,16 @@ private void cancelLoadingCurrentThumbnail() {
813811
// cancel the Picasso job associated with the player thumbnail, if any
814812
PicassoHelper.cancelTag(PICASSO_PLAYER_THUMBNAIL_TAG);
815813
}
814+
815+
private void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
816+
// Avoid useless thumbnail updates, if the thumbnail has not actually changed. Based on the
817+
// thumbnail loading code, this if would be skipped only when both bitmaps are `null`, since
818+
// onThumbnailLoaded won't be called twice with the same nonnull bitmap by Picasso's target.
819+
if (currentThumbnail != bitmap) {
820+
currentThumbnail = bitmap;
821+
UIs.call(playerUi -> playerUi.onThumbnailLoaded(bitmap));
822+
}
823+
}
816824
//endregion
817825

818826

@@ -1501,48 +1509,50 @@ public void onPlaybackSynchronize(@NonNull final PlayQueueItem item, final boole
15011509
Log.d(TAG, "Playback - onPlaybackSynchronize(was blocked: " + wasBlocked
15021510
+ ") called with item=[" + item.getTitle() + "], url=[" + item.getUrl() + "]");
15031511
}
1504-
if (exoPlayerIsNull() || playQueue == null) {
1505-
return;
1512+
if (exoPlayerIsNull() || playQueue == null || currentItem == item) {
1513+
return; // nothing to synchronize
15061514
}
15071515

1508-
final boolean hasPlayQueueItemChanged = currentItem != item;
1509-
1510-
final int currentPlayQueueIndex = playQueue.indexOf(item);
1511-
final int currentPlaylistIndex = simpleExoPlayer.getCurrentMediaItemIndex();
1512-
final int currentPlaylistSize = simpleExoPlayer.getCurrentTimeline().getWindowCount();
1516+
final int playQueueIndex = playQueue.indexOf(item);
1517+
final int playlistIndex = simpleExoPlayer.getCurrentMediaItemIndex();
1518+
final int playlistSize = simpleExoPlayer.getCurrentTimeline().getWindowCount();
1519+
final boolean removeThumbnailBeforeSync = currentItem == null
1520+
|| currentItem.getServiceId() != item.getServiceId()
1521+
|| !currentItem.getUrl().equals(item.getUrl());
15131522

1514-
// If nothing to synchronize
1515-
if (!hasPlayQueueItemChanged) {
1516-
return;
1517-
}
15181523
currentItem = item;
15191524

1520-
// Check if on wrong window
1521-
if (currentPlayQueueIndex != playQueue.getIndex()) {
1522-
Log.e(TAG, "Playback - Play Queue may be desynchronized: item "
1523-
+ "index=[" + currentPlayQueueIndex + "], "
1524-
+ "queue index=[" + playQueue.getIndex() + "]");
1525-
1526-
// Check if bad seek position
1527-
} else if ((currentPlaylistSize > 0 && currentPlayQueueIndex >= currentPlaylistSize)
1528-
|| currentPlayQueueIndex < 0) {
1529-
Log.e(TAG, "Playback - Trying to seek to invalid "
1530-
+ "index=[" + currentPlayQueueIndex + "] with "
1531-
+ "playlist length=[" + currentPlaylistSize + "]");
1532-
1533-
} else if (wasBlocked || currentPlaylistIndex != currentPlayQueueIndex || !isPlaying()) {
1525+
if (playQueueIndex != playQueue.getIndex()) {
1526+
// wrong window (this should be impossible, as this method is called with
1527+
// `item=playQueue.getItem()`, so the index of that item must be equal to `getIndex()`)
1528+
Log.e(TAG, "Playback - Play Queue may be not in sync: item index=["
1529+
+ playQueueIndex + "], " + "queue index=[" + playQueue.getIndex() + "]");
1530+
1531+
} else if ((playlistSize > 0 && playQueueIndex >= playlistSize) || playQueueIndex < 0) {
1532+
// the queue and the player's timeline are not in sync, since the play queue index
1533+
// points outside of the timeline
1534+
Log.e(TAG, "Playback - Trying to seek to invalid index=[" + playQueueIndex
1535+
+ "] with playlist length=[" + playlistSize + "]");
1536+
1537+
} else if (wasBlocked || playlistIndex != playQueueIndex || !isPlaying()) {
1538+
// either the player needs to be unblocked, or the play queue index has just been
1539+
// changed and needs to be synchronized, or the player is not playing
15341540
if (DEBUG) {
1535-
Log.d(TAG, "Playback - Rewinding to correct "
1536-
+ "index=[" + currentPlayQueueIndex + "], "
1537-
+ "from=[" + currentPlaylistIndex + "], "
1538-
+ "size=[" + currentPlaylistSize + "].");
1541+
Log.d(TAG, "Playback - Rewinding to correct index=[" + playQueueIndex + "], "
1542+
+ "from=[" + playlistIndex + "], size=[" + playlistSize + "].");
1543+
}
1544+
1545+
if (removeThumbnailBeforeSync) {
1546+
// unset the current (now outdated) thumbnail to ensure it is not used during sync
1547+
onThumbnailLoaded(null);
15391548
}
15401549

1550+
// sync the player index with the queue index, and seek to the correct position
15411551
if (item.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
1542-
simpleExoPlayer.seekTo(currentPlayQueueIndex, item.getRecoveryPosition());
1543-
playQueue.unsetRecovery(currentPlayQueueIndex);
1552+
simpleExoPlayer.seekTo(playQueueIndex, item.getRecoveryPosition());
1553+
playQueue.unsetRecovery(playQueueIndex);
15441554
} else {
1545-
simpleExoPlayer.seekToDefaultPosition(currentPlayQueueIndex);
1555+
simpleExoPlayer.seekToDefaultPosition(playQueueIndex);
15461556
}
15471557
}
15481558
}

app/src/main/java/org/schabi/newpipe/player/notification/NotificationPlayerUi.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void destroy() {
4343
@Override
4444
public void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
4545
super.onThumbnailLoaded(bitmap);
46-
notificationUtil.createNotificationIfNeededAndUpdate(false);
46+
notificationUtil.updateThumbnail();
4747
}
4848

4949
@Override

app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.schabi.newpipe.util.NavigationHelper;
2525

2626
import java.util.List;
27+
import java.util.Objects;
28+
import java.util.Optional;
2729

2830
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
2931
import static androidx.media.app.NotificationCompat.MediaStyle;
@@ -40,8 +42,6 @@
4042

4143
/**
4244
* This is a utility class for player notifications.
43-
*
44-
* @author cool-student
4545
*/
4646
public final class NotificationUtil {
4747
private static final String TAG = NotificationUtil.class.getSimpleName();
@@ -79,6 +79,19 @@ public synchronized void createNotificationIfNeededAndUpdate(final boolean force
7979
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
8080
}
8181

82+
public synchronized void updateThumbnail() {
83+
if (notificationBuilder != null) {
84+
if (DEBUG) {
85+
Log.d(TAG, "updateThumbnail() called with thumbnail = [" + Integer.toHexString(
86+
Optional.ofNullable(player.getThumbnail()).map(Objects::hashCode).orElse(0))
87+
+ "], title = [" + player.getVideoTitle() + "]");
88+
}
89+
90+
setLargeIcon(notificationBuilder);
91+
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
92+
}
93+
}
94+
8295
private synchronized NotificationCompat.Builder createNotification() {
8396
if (DEBUG) {
8497
Log.d(TAG, "createNotification()");
@@ -123,6 +136,9 @@ private synchronized NotificationCompat.Builder createNotification() {
123136
.setDeleteIntent(PendingIntent.getBroadcast(player.getContext(), NOTIFICATION_ID,
124137
new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT));
125138

139+
// set the initial value for the video thumbnail, updatable with updateNotificationThumbnail
140+
setLargeIcon(builder);
141+
126142
return builder;
127143
}
128144

@@ -142,7 +158,6 @@ private synchronized void updateNotification() {
142158
notificationBuilder.setTicker(player.getVideoTitle());
143159

144160
updateActions(notificationBuilder);
145-
setLargeIcon(notificationBuilder);
146161
}
147162

148163

app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java

+7-13
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ public abstract class VideoPlayerUi extends PlayerUi
109109
private final Handler controlsVisibilityHandler = new Handler(Looper.getMainLooper());
110110
@Nullable private SurfaceHolderCallback surfaceHolderCallback;
111111
boolean surfaceIsSetup = false;
112-
@Nullable private Bitmap thumbnail = null;
113112

114113

115114
/*//////////////////////////////////////////////////////////////////////////
@@ -385,9 +384,7 @@ public void destroyPlayer() {
385384
@Override
386385
public void destroy() {
387386
super.destroy();
388-
if (binding != null) {
389-
binding.endScreen.setImageBitmap(null);
390-
}
387+
binding.endScreen.setImageDrawable(null);
391388
deinitPlayerSeekOverlay();
392389
deinitListeners();
393390
}
@@ -422,12 +419,10 @@ protected void setupElementsSize(final int buttonsMinWidth,
422419
public void onBroadcastReceived(final Intent intent) {
423420
super.onBroadcastReceived(intent);
424421
if (Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
425-
// When the orientation changed, the screen height might be smaller.
426-
// If the end screen thumbnail is not re-scaled,
427-
// it can be larger than the current screen height
428-
// and thus enlarging the whole player.
429-
// This causes the seekbar to be ouf the visible area.
430-
updateEndScreenThumbnail();
422+
// When the orientation changes, the screen height might be smaller. If the end screen
423+
// thumbnail is not re-scaled, it can be larger than the current screen height and thus
424+
// enlarging the whole player. This causes the seekbar to be out of the visible area.
425+
updateEndScreenThumbnail(player.getThumbnail());
431426
}
432427
}
433428
//endregion
@@ -449,11 +444,10 @@ public void onBroadcastReceived(final Intent intent) {
449444
@Override
450445
public void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
451446
super.onThumbnailLoaded(bitmap);
452-
thumbnail = bitmap;
453-
updateEndScreenThumbnail();
447+
updateEndScreenThumbnail(bitmap);
454448
}
455449

456-
private void updateEndScreenThumbnail() {
450+
private void updateEndScreenThumbnail(@Nullable final Bitmap thumbnail) {
457451
if (thumbnail == null) {
458452
// remove end screen thumbnail
459453
binding.endScreen.setImageDrawable(null);

0 commit comments

Comments
 (0)