From 62c81ab8428c3a34c0d4ec264ab2897bd3c41b0c Mon Sep 17 00:00:00 2001 From: Mimmo Date: Tue, 16 Jun 2026 12:01:52 +0200 Subject: [PATCH] fix(player): show clear-queue confirmation when tapping a new video The "ask for confirmation before clearing a queue" setting only worked when switching the same video between player types (popup/background/ main player). It never fired when tapping a different video from the feed, search results, or the related-items list under an expanded player, which is the setting's main use case. Root cause: selectAndLoadVideo() (the entry point used by all of the above) had no confirmation check at all. By the time the existing replaceQueueIfUserConfirms() check ran later in the flow (via runWorker's autoplay path), hideMainPlayerOnLoadingNewStream() had already torn down the active player/queue, so the check always saw no active queue to protect. Add loadVideoIfUserConfirms(), evaluated synchronously at the top of selectAndLoadVideo() before anything is torn down. It compares the live player's current queue item against the URL about to be loaded, since at this point playQueue/url still reflect the *old* target. Also only ask for confirmation when the active queue actually has more than one item; replacing a lone, queue-less video should just play the new one immediately. --- .../fragments/detail/VideoDetailFragment.java | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index c9ac6aa33..7616ad35e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -996,14 +996,17 @@ public void selectAndLoadVideo(final int newServiceId, @Nullable final String newUrl, @NonNull final String newTitle, @Nullable final PlayQueue newQueue) { - if (isPlayerAvailable() && newQueue != null && playQueue != null - && playQueue.getItem() != null && !playQueue.getItem().getUrl().equals(newUrl)) { - // Preloading can be disabled since playback is surely being replaced. - player.disablePreloadingOfCurrentTrack(); - } + loadVideoIfUserConfirms(newUrl, () -> { + if (isPlayerAvailable() && newQueue != null && playQueue != null + && playQueue.getItem() != null + && !playQueue.getItem().getUrl().equals(newUrl)) { + // Preloading can be disabled since playback is surely being replaced. + player.disablePreloadingOfCurrentTrack(); + } - setInitialData(newServiceId, newUrl, newTitle, newQueue); - startLoading(false, true); + setInitialData(newServiceId, newUrl, newTitle, newQueue); + startLoading(false, true); + }); } private void prepareAndHandleInfoIfNeededAfterDelay(final StreamInfo info, @@ -2510,10 +2513,12 @@ private StackItem findQueueInStack(final PlayQueue queue) { private void replaceQueueIfUserConfirms(final Runnable onAllow) { @Nullable final PlayQueue activeQueue = isPlayerAvailable() ? player.getPlayQueue() : null; + final boolean hasMultiItemQueue = activeQueue != null && activeQueue.size() > 1; // Player will have STATE_IDLE when a user pressed back button if (isClearingQueueConfirmationRequired(activity) && playerIsNotStopped() + && hasMultiItemQueue && activeQueue != null && !activeQueue.equals(playQueue)) { showClearingQueueConfirmation(onAllow); @@ -2522,6 +2527,33 @@ && playerIsNotStopped() } } + /** + * Same purpose as {@link #replaceQueueIfUserConfirms(Runnable)}, but meant to be called + * before the new stream's data has been loaded (e.g. from {@link #selectAndLoadVideo}), + * i.e. before {@link #playQueue} has been updated to reflect the new target. Since + * {@link #playQueue} cannot be used for comparison yet, the queue currently playing in the + * active player is compared directly against the URL of the stream about to be loaded. + * + * @param newUrl the url of the stream that is about to replace the active one, if confirmed + * @param onAllow the action to run once the navigation is allowed to proceed + */ + private void loadVideoIfUserConfirms(@Nullable final String newUrl, final Runnable onAllow) { + @Nullable final PlayQueue activeQueue = isPlayerAvailable() ? player.getPlayQueue() : null; + @Nullable final PlayQueueItem activeItem = + activeQueue != null ? activeQueue.getItem() : null; + final boolean hasMultiItemQueue = activeQueue != null && activeQueue.size() > 1; + + if (isClearingQueueConfirmationRequired(activity) + && playerIsNotStopped() + && hasMultiItemQueue + && activeItem != null + && !activeItem.getUrl().equals(newUrl)) { + showClearingQueueConfirmation(onAllow); + } else { + onAllow.run(); + } + } + private void showClearingQueueConfirmation(final Runnable onAllow) { new AlertDialog.Builder(activity) .setTitle(R.string.clear_queue_confirmation_description)