From 16f8f855b6fc62e0907f82015389f3845982a76a Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 9 Feb 2026 23:36:49 -0500 Subject: [PATCH 1/2] Add fullscreen support for `VideoPlayer` --- Sources/SkipAV/VideoPlayer.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/SkipAV/VideoPlayer.swift b/Sources/SkipAV/VideoPlayer.swift index 4a77fed..24d6d14 100644 --- a/Sources/SkipAV/VideoPlayer.swift +++ b/Sources/SkipAV/VideoPlayer.swift @@ -17,10 +17,20 @@ import androidx.media3.ui.compose.state.rememberPresentationState import androidx.compose.ui.platform.LocalContext public struct VideoPlayer: View { + let player: AVPlayer + + var isFullscreen: Binding? public init(player: AVPlayer) { self.player = player + self.isFullscreen = nil + } + + /// Android-only API to handle fullscreen button + public init(player: AVPlayer, isFullscreen: Binding) { + self.player = player + self.isFullscreen = isFullscreen } // SKIP @nobridge @@ -39,6 +49,12 @@ public struct VideoPlayer: View { playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT playerView.controllerAutoShow = false // hide controls initially, like on iOS playerView.player = player.mediaPlayer + // Enable fullscreen button and handle fullscreen transitions + if let isFullscreenBinding = self.isFullscreen { + playerView.setControllerOnFullScreenModeChangedListener { isFullScreen in + isFullscreenBinding.wrappedValue = isFullScreen + } + } return playerView }, modifier: modifier, update: { playerView in }) From 7c6008dc04c56b09ffc5cacc8f9088f9e9b10a8d Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 9 Feb 2026 23:44:56 -0500 Subject: [PATCH 2/2] Add `AVPlayer.timeControlStatus` --- Sources/SkipAV/AVPlayer.swift | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Sources/SkipAV/AVPlayer.swift b/Sources/SkipAV/AVPlayer.swift index cedc4bd..584c4f2 100644 --- a/Sources/SkipAV/AVPlayer.swift +++ b/Sources/SkipAV/AVPlayer.swift @@ -85,6 +85,66 @@ public class AVPlayer { } return AVPlayerItem(asset: AVAsset(mediaItem: mediaItem)) } + + /// Indicates whether playback is currently paused indefinitely, suspended while waiting for appropriate conditions, or in progress. + public var timeControlStatus: TimeControlStatus { + let playbackState = mediaPlayer.playbackState + let playWhenReady = mediaPlayer.playWhenReady + + // Map Media3 states to AVPlayer.TimeControlStatus + switch playbackState { + case Player.STATE_IDLE: + return .paused + case Player.STATE_BUFFERING: + // If playWhenReady is true, we're waiting to play + return playWhenReady ? .waitingToPlayAtSpecifiedRate : .paused + case Player.STATE_READY: + // If playWhenReady is true and playback speed > 0, we're playing + return playWhenReady ? .playing : .paused + case Player.STATE_ENDED: + return .paused + default: + return .paused + } + } + + /// Constants that describe the current time control status. + public enum TimeControlStatus: Int, Sendable { + /// Playback is currently paused indefinitely. + case paused = 0 + + /// Playback is currently waiting for appropriate network or other conditions before starting. + case waitingToPlayAtSpecifiedRate = 1 + + /// Playback is currently in progress. + case playing = 2 + } + + /// The reason the player is waiting for playback to continue. + /// + /// This property is only meaningful when timeControlStatus is waitingToPlayAtSpecifiedRate. + public var reasonForWaitingToPlay: WaitingReason? { + let playbackState = mediaPlayer.playbackState + + if playbackState == Player.STATE_BUFFERING && mediaPlayer.playWhenReady { + // Check if we're buffering due to network conditions + return .toMinimizeStalls + } + + return nil + } + + /// Constants that describe why the player is in a waiting state. + public enum WaitingReason: String, Sendable { + /// The player is waiting for more data to be buffered before playback can continue. + case toMinimizeStalls = "AVPlayerWaitingToMinimizeStallsReason" + + /// The player is waiting because it requires evaluation of media composition. + case evaluatingBufferingRate = "AVPlayerWaitingWhileEvaluatingBufferingRateReason" + + /// The player is waiting for another item in the queue. + case noItemToPlay = "AVPlayerWaitingWithNoItemToPlayReason" + } public init() { }