diff --git a/app/src/main/java/app/revanced/integrations/patches/PlayerOverlaysHookPatch.java b/app/src/main/java/app/revanced/integrations/patches/PlayerOverlaysHookPatch.java index 95b30835..4f1969df 100644 --- a/app/src/main/java/app/revanced/integrations/patches/PlayerOverlaysHookPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/PlayerOverlaysHookPatch.java @@ -15,7 +15,7 @@ import app.revanced.integrations.shared.PlayerOverlays; @SuppressWarnings("unused") public class PlayerOverlaysHookPatch { /** - * Hook into YouTubePlayerOverlaysLayout.onFinishInflate() method + * Injection point. * * @param thisRef reference to the view * @smali YouTubePlayerOverlaysLayout_onFinishInflateHook(Ljava / lang / Object ;)V diff --git a/app/src/main/java/app/revanced/integrations/patches/PlayerTypeHookPatch.java b/app/src/main/java/app/revanced/integrations/patches/PlayerTypeHookPatch.java index 2f46f008..49296e5c 100644 --- a/app/src/main/java/app/revanced/integrations/patches/PlayerTypeHookPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/PlayerTypeHookPatch.java @@ -3,32 +3,25 @@ package app.revanced.integrations.patches; import androidx.annotation.Nullable; import app.revanced.integrations.shared.PlayerType; -import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.shared.VideoState; -/** - * Hook receiver class for 'player-type-hook' patch - * - * @usedBy app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch - * @smali Lapp/revanced/integrations/patches/PlayerTypeHookPatch; - */ @SuppressWarnings("unused") public class PlayerTypeHookPatch { /** - * Hook into YouTubePlayerOverlaysLayout.updatePlayerLayout() method - * - * @param type the new player type - * @smali YouTubePlayerOverlaysLayout_updatePlayerTypeHookEX(Ljava/lang/Object;)V + * Injection point. */ - public static void YouTubePlayerOverlaysLayout_updatePlayerTypeHookEX(@Nullable Object type) { - if (type == null) return; + public static void setPlayerType(@Nullable Enum youTubePlayerType) { + if (youTubePlayerType == null) return; - // update current player type - final PlayerType newType = PlayerType.safeParseFromString(type.toString()); - if (newType == null) { - LogHelper.printException(() -> "Unknown PlayerType encountered: " + type); - } else { - PlayerType.setCurrent(newType); - LogHelper.printDebug(() -> "PlayerType was updated to: " + newType); - } + PlayerType.setFromString(youTubePlayerType.name()); + } + + /** + * Injection point. + */ + public static void setVideoState(@Nullable Enum youTubeVideoState) { + if (youTubeVideoState == null) return; + + VideoState.setFromString(youTubeVideoState.name()); } } diff --git a/app/src/main/java/app/revanced/integrations/patches/VideoInformation.java b/app/src/main/java/app/revanced/integrations/patches/VideoInformation.java index e5822fa7..2a321953 100644 --- a/app/src/main/java/app/revanced/integrations/patches/VideoInformation.java +++ b/app/src/main/java/app/revanced/integrations/patches/VideoInformation.java @@ -7,6 +7,7 @@ import java.lang.reflect.Method; import java.util.Objects; import app.revanced.integrations.patches.playback.speed.RememberPlaybackSpeedPatch; +import app.revanced.integrations.shared.VideoState; import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; @@ -183,7 +184,12 @@ public final class VideoInformation { * @return If the playback is at the end of the video. * * If video is playing in the background with no video visible, - * this always returns false (even if the video is actually at the end) + * this always returns false (even if the video is actually at the end). + * + * This is equivalent to checking for {@link VideoState#ENDED}, + * but can give a more up to date result for code calling from some hooks. + * + * @see VideoState */ public static boolean isAtEndOfVideo() { return videoTime > 0 && videoLength > 0 && videoTime >= videoLength; diff --git a/app/src/main/java/app/revanced/integrations/shared/PlayerType.kt b/app/src/main/java/app/revanced/integrations/shared/PlayerType.kt index 47d2a097..4992170f 100644 --- a/app/src/main/java/app/revanced/integrations/shared/PlayerType.kt +++ b/app/src/main/java/app/revanced/integrations/shared/PlayerType.kt @@ -1,11 +1,11 @@ package app.revanced.integrations.shared import app.revanced.integrations.utils.Event +import app.revanced.integrations.utils.LogHelper /** * WatchWhile player type */ -@Suppress("unused") enum class PlayerType { /** * Includes Shorts and Stories playback. @@ -40,15 +40,15 @@ enum class PlayerType { private val nameToPlayerType = values().associateBy { it.name } - /** - * safely parse from a string - * - * @param name the name to find - * @return the enum constant, or null if not found - */ @JvmStatic - fun safeParseFromString(name: String): PlayerType? { - return nameToPlayerType[name] + fun setFromString(enumName: String) { + val newType = nameToPlayerType[enumName] + if (newType == null) { + LogHelper.printException { "Unknown PlayerType encountered: $enumName" } + } else if (current != newType) { + LogHelper.printDebug { "PlayerType changed to: $newType" } + current = newType + } } /** @@ -57,7 +57,7 @@ enum class PlayerType { @JvmStatic var current get() = currentPlayerType - set(value) { + private set(value) { currentPlayerType = value onChange(currentPlayerType) } @@ -90,7 +90,7 @@ enum class PlayerType { * although can return false positive if the player is minimized. * * @return If nothing, a Short, a Story, - * or a regular minimized video is sliding off screen to a dismissed or hidden state. + * or a regular video is minimized video or sliding off screen to a dismissed or hidden state. */ fun isNoneHiddenOrMinimized(): Boolean { return this == NONE || this == HIDDEN diff --git a/app/src/main/java/app/revanced/integrations/shared/VideoState.kt b/app/src/main/java/app/revanced/integrations/shared/VideoState.kt new file mode 100644 index 00000000..977b9181 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/shared/VideoState.kt @@ -0,0 +1,48 @@ +package app.revanced.integrations.shared + +import app.revanced.integrations.utils.LogHelper +import app.revanced.integrations.patches.VideoInformation + +/** + * VideoState playback state. + */ +enum class VideoState { + NEW, + PLAYING, + PAUSED, + RECOVERABLE_ERROR, + UNRECOVERABLE_ERROR, + /** + * @see [VideoInformation.isAtEndOfVideo] + */ + ENDED; + + companion object { + + private val nameToVideoState = values().associateBy { it.name } + + @JvmStatic + fun setFromString(enumName: String) { + val state = nameToVideoState[enumName] + if (state == null) { + LogHelper.printException { "Unknown VideoState encountered: $enumName" } + } else if (currentVideoState != state) { + LogHelper.printDebug { "VideoState changed to: $state" } + currentVideoState = state + } + } + + /** + * Depending on which hook this is called from, + * this value may not be up to date with the actual playback state. + */ + @JvmStatic + var current: VideoState? + get() = currentVideoState + private set(value) { + currentVideoState = value + } + + private var currentVideoState : VideoState? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/sponsorblock/SegmentPlaybackController.java b/app/src/main/java/app/revanced/integrations/sponsorblock/SegmentPlaybackController.java index 57252b4b..ee96db04 100644 --- a/app/src/main/java/app/revanced/integrations/sponsorblock/SegmentPlaybackController.java +++ b/app/src/main/java/app/revanced/integrations/sponsorblock/SegmentPlaybackController.java @@ -20,6 +20,7 @@ import java.util.Objects; import app.revanced.integrations.patches.VideoInformation; import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.shared.PlayerType; +import app.revanced.integrations.shared.VideoState; import app.revanced.integrations.sponsorblock.objects.CategoryBehaviour; import app.revanced.integrations.sponsorblock.objects.SegmentCategory; import app.revanced.integrations.sponsorblock.objects.SponsorSegment; @@ -522,6 +523,7 @@ public class SegmentPlaybackController { return; } + final boolean videoIsPaused = VideoState.getCurrent() == VideoState.PAUSED; if (!userManuallySkipped) { // check for any smaller embedded segments, and count those as autoskipped final boolean showSkipToast = SettingsEnum.SB_TOAST_ON_SKIP.getBoolean(); @@ -532,7 +534,10 @@ public class SegmentPlaybackController { if (otherSegment == segmentToSkip || (otherSegment.category != SegmentCategory.HIGHLIGHT && segmentToSkip.containsSegment(otherSegment))) { otherSegment.didAutoSkipped = true; - if (showSkipToast) { + // Do not show a toast if the user is scrubbing thru a paused video. + // Cannot do this video state check in setTime or earlier in this method, as the video state may not be up to date. + // So instead, only hide toasts because all other skip logic done while paused causes no harm. + if (showSkipToast && !videoIsPaused) { showSkippedSegmentToast(otherSegment); } } @@ -542,7 +547,7 @@ public class SegmentPlaybackController { if (segmentToSkip.category == SegmentCategory.UNSUBMITTED) { removeUnsubmittedSegments(); SponsorBlockUtils.setNewSponsorSegmentPreviewed(); - } else { + } else if (!videoIsPaused) { SponsorBlockUtils.sendViewRequestAsync(segmentToSkip); } } catch (Exception ex) {