[Video] Add `timeRemainingChange` event to `player` in `expo-video` (#5013)
parent
d92731b1eb
commit
d52d29621e
|
@ -1,3 +1,27 @@
|
||||||
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
|
||||||
|
index 473f964..f37aff9 100644
|
||||||
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
|
||||||
|
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
|
||||||
|
@@ -41,6 +41,11 @@ sealed class PlayerEvent {
|
||||||
|
override val name = "playToEnd"
|
||||||
|
}
|
||||||
|
|
||||||
|
+ data class PlayerTimeRemainingChanged(val timeRemaining: Double): PlayerEvent() {
|
||||||
|
+ override val name = "timeRemainingChange"
|
||||||
|
+ override val arguments = arrayOf(timeRemaining)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
fun emit(player: VideoPlayer, listeners: List<VideoPlayerListener>) {
|
||||||
|
when (this) {
|
||||||
|
is StatusChanged -> listeners.forEach { it.onStatusChanged(player, status, oldStatus, error) }
|
||||||
|
@@ -49,6 +54,7 @@ sealed class PlayerEvent {
|
||||||
|
is SourceChanged -> listeners.forEach { it.onSourceChanged(player, source, oldSource) }
|
||||||
|
is PlaybackRateChanged -> listeners.forEach { it.onPlaybackRateChanged(player, rate, oldRate) }
|
||||||
|
is PlayedToEnd -> listeners.forEach { it.onPlayedToEnd(player) }
|
||||||
|
+ is PlayerTimeRemainingChanged -> listeners.forEach { it.onPlayerTimeRemainingChanged(player, timeRemaining) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
|
||||||
index 9905e13..47342ff 100644
|
index 9905e13..47342ff 100644
|
||||||
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
|
||||||
|
@ -8,10 +32,10 @@ index 9905e13..47342ff 100644
|
||||||
setTimeBarInteractive(requireLinearPlayback)
|
setTimeBarInteractive(requireLinearPlayback)
|
||||||
+ setShowSubtitleButton(true)
|
+ setShowSubtitleButton(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
@@ -27,7 +28,8 @@ internal fun PlayerView.setTimeBarInteractive(interactive: Boolean) {
|
@@ -27,7 +28,8 @@ internal fun PlayerView.setTimeBarInteractive(interactive: Boolean) {
|
||||||
|
|
||||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
internal fun PlayerView.setFullscreenButtonVisibility(visible: Boolean) {
|
internal fun PlayerView.setFullscreenButtonVisibility(visible: Boolean) {
|
||||||
- val fullscreenButton = findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
|
- val fullscreenButton = findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
|
||||||
|
@ -20,6 +44,42 @@ index 9905e13..47342ff 100644
|
||||||
fullscreenButton?.visibility = if (visible) {
|
fullscreenButton?.visibility = if (visible) {
|
||||||
android.view.View.VISIBLE
|
android.view.View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..0249e23
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt
|
||||||
|
@@ -0,0 +1,29 @@
|
||||||
|
+import android.os.Handler
|
||||||
|
+import android.os.Looper
|
||||||
|
+import androidx.annotation.OptIn
|
||||||
|
+import androidx.media3.common.util.UnstableApi
|
||||||
|
+import expo.modules.video.PlayerEvent
|
||||||
|
+import expo.modules.video.VideoPlayer
|
||||||
|
+import kotlin.math.floor
|
||||||
|
+
|
||||||
|
+@OptIn(UnstableApi::class)
|
||||||
|
+class ProgressTracker(private val videoPlayer: VideoPlayer) : Runnable {
|
||||||
|
+ private val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
|
+ private val player = videoPlayer.player
|
||||||
|
+
|
||||||
|
+ init {
|
||||||
|
+ handler.post(this)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ override fun run() {
|
||||||
|
+ val currentPosition = player.currentPosition
|
||||||
|
+ val duration = player.duration
|
||||||
|
+ val timeRemaining = floor(((duration - currentPosition) / 1000).toDouble())
|
||||||
|
+ videoPlayer.sendEvent(PlayerEvent.PlayerTimeRemainingChanged(timeRemaining))
|
||||||
|
+ handler.postDelayed(this, 1000 /* ms */)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fun remove() {
|
||||||
|
+ handler.removeCallbacks(this)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
\ No newline at end of file
|
||||||
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
|
||||||
index ec3da2a..5a1397a 100644
|
index ec3da2a..5a1397a 100644
|
||||||
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
|
||||||
|
@ -33,8 +93,76 @@ index ec3da2a..5a1397a 100644
|
||||||
+ "onEnterFullscreen",
|
+ "onEnterFullscreen",
|
||||||
+ "onExitFullscreen"
|
+ "onExitFullscreen"
|
||||||
)
|
)
|
||||||
|
|
||||||
Prop("player") { view: VideoView, player: VideoPlayer ->
|
Prop("player") { view: VideoView, player: VideoPlayer ->
|
||||||
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
|
||||||
|
index 58f00af..5ad8237 100644
|
||||||
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
|
||||||
|
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
|
||||||
|
@@ -1,5 +1,6 @@
|
||||||
|
package expo.modules.video
|
||||||
|
|
||||||
|
+import ProgressTracker
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.SurfaceView
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
@@ -35,11 +36,13 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
||||||
|
.Builder(context, renderersFactory)
|
||||||
|
.setLooper(context.mainLooper)
|
||||||
|
.build()
|
||||||
|
+ var progressTracker: ProgressTracker? = null
|
||||||
|
|
||||||
|
val serviceConnection = PlaybackServiceConnection(WeakReference(player))
|
||||||
|
|
||||||
|
var playing by IgnoreSameSet(false) { new, old ->
|
||||||
|
sendEvent(PlayerEvent.IsPlayingChanged(new, old))
|
||||||
|
+ addOrRemoveProgressTracker()
|
||||||
|
}
|
||||||
|
|
||||||
|
var uncommittedSource: VideoSource? = source
|
||||||
|
@@ -141,6 +144,9 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
+ this.progressTracker?.remove()
|
||||||
|
+ this.progressTracker = null
|
||||||
|
+
|
||||||
|
appContext?.reactContext?.unbindService(serviceConnection)
|
||||||
|
serviceConnection.playbackServiceBinder?.service?.unregisterPlayer(player)
|
||||||
|
VideoManager.unregisterVideoPlayer(this@VideoPlayer)
|
||||||
|
@@ -228,7 +234,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
||||||
|
listeners.removeAll { it.get() == videoPlayerListener }
|
||||||
|
}
|
||||||
|
|
||||||
|
- private fun sendEvent(event: PlayerEvent) {
|
||||||
|
+ fun sendEvent(event: PlayerEvent) {
|
||||||
|
// Emits to the native listeners
|
||||||
|
event.emit(this, listeners.mapNotNull { it.get() })
|
||||||
|
// Emits to the JS side
|
||||||
|
@@ -240,4 +246,13 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
||||||
|
sendEvent(eventName, *args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ private fun addOrRemoveProgressTracker() {
|
||||||
|
+ this.progressTracker?.remove()
|
||||||
|
+ if (this.playing) {
|
||||||
|
+ this.progressTracker = ProgressTracker(this)
|
||||||
|
+ } else {
|
||||||
|
+ this.progressTracker = null
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
|
||||||
|
index f654254..dcfe3f0 100644
|
||||||
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
|
||||||
|
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
|
||||||
|
@@ -15,4 +15,5 @@ interface VideoPlayerListener {
|
||||||
|
fun onSourceChanged(player: VideoPlayer, source: VideoSource?, oldSource: VideoSource?) {}
|
||||||
|
fun onPlaybackRateChanged(player: VideoPlayer, rate: Float, oldRate: Float?) {}
|
||||||
|
fun onPlayedToEnd(player: VideoPlayer) {}
|
||||||
|
+ fun onPlayerTimeRemainingChanged(player: VideoPlayer, timeRemaining: Double) {}
|
||||||
|
}
|
||||||
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
|
diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
|
||||||
index a951d80..3932535 100644
|
index a951d80..3932535 100644
|
||||||
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
|
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
|
||||||
|
@ -45,7 +173,7 @@ index a951d80..3932535 100644
|
||||||
val onPictureInPictureStop by EventDispatcher<Unit>()
|
val onPictureInPictureStop by EventDispatcher<Unit>()
|
||||||
+ val onEnterFullscreen by EventDispatcher()
|
+ val onEnterFullscreen by EventDispatcher()
|
||||||
+ val onExitFullscreen by EventDispatcher()
|
+ val onExitFullscreen by EventDispatcher()
|
||||||
|
|
||||||
var willEnterPiP: Boolean = false
|
var willEnterPiP: Boolean = false
|
||||||
var isInFullscreen: Boolean = false
|
var isInFullscreen: Boolean = false
|
||||||
@@ -154,6 +156,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
|
@@ -154,6 +156,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
|
||||||
|
@ -55,7 +183,7 @@ index a951d80..3932535 100644
|
||||||
+ onEnterFullscreen(mapOf())
|
+ onEnterFullscreen(mapOf())
|
||||||
isInFullscreen = true
|
isInFullscreen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +165,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
|
@@ -162,6 +165,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
|
||||||
val fullScreenButton: ImageButton = playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
|
val fullScreenButton: ImageButton = playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
|
||||||
fullScreenButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter)
|
fullScreenButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter)
|
||||||
|
@ -63,9 +191,22 @@ index a951d80..3932535 100644
|
||||||
+ this.onExitFullscreen(mapOf())
|
+ this.onExitFullscreen(mapOf())
|
||||||
isInFullscreen = false
|
isInFullscreen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diff --git a/node_modules/expo-video/build/VideoPlayer.types.d.ts b/node_modules/expo-video/build/VideoPlayer.types.d.ts
|
||||||
|
index a09fcfe..65fe29a 100644
|
||||||
|
--- a/node_modules/expo-video/build/VideoPlayer.types.d.ts
|
||||||
|
+++ b/node_modules/expo-video/build/VideoPlayer.types.d.ts
|
||||||
|
@@ -128,6 +128,8 @@ export type VideoPlayerEvents = {
|
||||||
|
* Handler for an event emitted when the current media source of the player changes.
|
||||||
|
*/
|
||||||
|
sourceChange(newSource: VideoSource, previousSource: VideoSource): void;
|
||||||
|
+
|
||||||
|
+ timeRemainingChange(timeRemaining: number): void;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the current status of the player.
|
||||||
diff --git a/node_modules/expo-video/build/VideoView.types.d.ts b/node_modules/expo-video/build/VideoView.types.d.ts
|
diff --git a/node_modules/expo-video/build/VideoView.types.d.ts b/node_modules/expo-video/build/VideoView.types.d.ts
|
||||||
index cb9ca6d..60e9f4e 100644
|
index cb9ca6d..ed8bb7e 100644
|
||||||
--- a/node_modules/expo-video/build/VideoView.types.d.ts
|
--- a/node_modules/expo-video/build/VideoView.types.d.ts
|
||||||
+++ b/node_modules/expo-video/build/VideoView.types.d.ts
|
+++ b/node_modules/expo-video/build/VideoView.types.d.ts
|
||||||
@@ -89,5 +89,8 @@ export interface VideoViewProps extends ViewProps {
|
@@ -89,5 +89,8 @@ export interface VideoViewProps extends ViewProps {
|
||||||
|
@ -77,6 +218,7 @@ index cb9ca6d..60e9f4e 100644
|
||||||
+ onExitFullscreen?: () => void;
|
+ onExitFullscreen?: () => void;
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=VideoView.types.d.ts.map
|
//# sourceMappingURL=VideoView.types.d.ts.map
|
||||||
|
\ No newline at end of file
|
||||||
diff --git a/node_modules/expo-video/ios/VideoModule.swift b/node_modules/expo-video/ios/VideoModule.swift
|
diff --git a/node_modules/expo-video/ios/VideoModule.swift b/node_modules/expo-video/ios/VideoModule.swift
|
||||||
index c537a12..e4a918f 100644
|
index c537a12..e4a918f 100644
|
||||||
--- a/node_modules/expo-video/ios/VideoModule.swift
|
--- a/node_modules/expo-video/ios/VideoModule.swift
|
||||||
|
@ -90,19 +232,111 @@ index c537a12..e4a918f 100644
|
||||||
+ "onEnterFullscreen",
|
+ "onEnterFullscreen",
|
||||||
+ "onExitFullscreen"
|
+ "onExitFullscreen"
|
||||||
)
|
)
|
||||||
|
|
||||||
Prop("player") { (view, player: VideoPlayer?) in
|
Prop("player") { (view, player: VideoPlayer?) in
|
||||||
|
diff --git a/node_modules/expo-video/ios/VideoPlayer.swift b/node_modules/expo-video/ios/VideoPlayer.swift
|
||||||
|
index 3315b88..f482390 100644
|
||||||
|
--- a/node_modules/expo-video/ios/VideoPlayer.swift
|
||||||
|
+++ b/node_modules/expo-video/ios/VideoPlayer.swift
|
||||||
|
@@ -185,6 +185,10 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
||||||
|
safeEmit(event: "sourceChange", arguments: newVideoPlayerItem?.videoSource, oldVideoPlayerItem?.videoSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
+ func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double) {
|
||||||
|
+ safeEmit(event: "timeRemainingChange", arguments: timeRemaining)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
func safeEmit<each A: AnyArgument>(event: String, arguments: repeat each A) {
|
||||||
|
if self.appContext != nil {
|
||||||
|
self.emit(event: event, arguments: repeat each arguments)
|
||||||
|
diff --git a/node_modules/expo-video/ios/VideoPlayerObserver.swift b/node_modules/expo-video/ios/VideoPlayerObserver.swift
|
||||||
|
index d289e26..d0fdd30 100644
|
||||||
|
--- a/node_modules/expo-video/ios/VideoPlayerObserver.swift
|
||||||
|
+++ b/node_modules/expo-video/ios/VideoPlayerObserver.swift
|
||||||
|
@@ -21,6 +21,7 @@ protocol VideoPlayerObserverDelegate: AnyObject {
|
||||||
|
func onItemChanged(player: AVPlayer, oldVideoPlayerItem: VideoPlayerItem?, newVideoPlayerItem: VideoPlayerItem?)
|
||||||
|
func onIsMutedChanged(player: AVPlayer, oldIsMuted: Bool?, newIsMuted: Bool)
|
||||||
|
func onPlayerItemStatusChanged(player: AVPlayer, oldStatus: AVPlayerItem.Status?, newStatus: AVPlayerItem.Status)
|
||||||
|
+ func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default implementations for the delegate
|
||||||
|
@@ -33,6 +34,7 @@ extension VideoPlayerObserverDelegate {
|
||||||
|
func onItemChanged(player: AVPlayer, oldVideoPlayerItem: VideoPlayerItem?, newVideoPlayerItem: VideoPlayerItem?) {}
|
||||||
|
func onIsMutedChanged(player: AVPlayer, oldIsMuted: Bool?, newIsMuted: Bool) {}
|
||||||
|
func onPlayerItemStatusChanged(player: AVPlayer, oldStatus: AVPlayerItem.Status?, newStatus: AVPlayerItem.Status) {}
|
||||||
|
+ func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper used to store WeakReferences to the observer delegate
|
||||||
|
@@ -91,6 +93,7 @@ class VideoPlayerObserver {
|
||||||
|
private var playerVolumeObserver: NSKeyValueObservation?
|
||||||
|
private var playerCurrentItemObserver: NSKeyValueObservation?
|
||||||
|
private var playerIsMutedObserver: NSKeyValueObservation?
|
||||||
|
+ private var playerPeriodicTimeObserver: Any?
|
||||||
|
|
||||||
|
// Current player item observers
|
||||||
|
private var playbackBufferEmptyObserver: NSKeyValueObservation?
|
||||||
|
@@ -152,6 +155,9 @@ class VideoPlayerObserver {
|
||||||
|
playerVolumeObserver?.invalidate()
|
||||||
|
playerIsMutedObserver?.invalidate()
|
||||||
|
playerCurrentItemObserver?.invalidate()
|
||||||
|
+ if let playerPeriodicTimeObserver = self.playerPeriodicTimeObserver {
|
||||||
|
+ player?.removeTimeObserver(playerPeriodicTimeObserver)
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initializeCurrentPlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) {
|
||||||
|
@@ -270,6 +276,7 @@ class VideoPlayerObserver {
|
||||||
|
|
||||||
|
if isPlaying != (player.timeControlStatus == .playing) {
|
||||||
|
isPlaying = player.timeControlStatus == .playing
|
||||||
|
+ addOrRemovePeriodicTimeObserver()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -310,4 +317,30 @@ class VideoPlayerObserver {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ private func onPlayerTimeRemainingChanged(_ player: AVPlayer, _ timeRemaining: Double) {
|
||||||
|
+ delegates.forEach { delegate in
|
||||||
|
+ delegate.value?.onPlayerTimeRemainingChanged(player: player, timeRemaining: timeRemaining)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private func addOrRemovePeriodicTimeObserver() {
|
||||||
|
+ guard let player = self.player else {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if isPlaying {
|
||||||
|
+ // Add the time update listener
|
||||||
|
+ playerPeriodicTimeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1.0, preferredTimescale: Int32(NSEC_PER_SEC)), queue: nil) { event in
|
||||||
|
+ guard let duration = player.currentItem?.duration else {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let timeRemaining = (duration.seconds - event.seconds).rounded()
|
||||||
|
+ self.onPlayerTimeRemainingChanged(player, timeRemaining)
|
||||||
|
+ }
|
||||||
|
+ } else if let playerPeriodicTimeObserver = self.playerPeriodicTimeObserver {
|
||||||
|
+ player.removeTimeObserver(playerPeriodicTimeObserver)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
diff --git a/node_modules/expo-video/ios/VideoView.swift b/node_modules/expo-video/ios/VideoView.swift
|
diff --git a/node_modules/expo-video/ios/VideoView.swift b/node_modules/expo-video/ios/VideoView.swift
|
||||||
index f4579e4..10c5908 100644
|
index f4579e4..10c5908 100644
|
||||||
--- a/node_modules/expo-video/ios/VideoView.swift
|
--- a/node_modules/expo-video/ios/VideoView.swift
|
||||||
+++ b/node_modules/expo-video/ios/VideoView.swift
|
+++ b/node_modules/expo-video/ios/VideoView.swift
|
||||||
@@ -41,6 +41,8 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
@@ -41,6 +41,8 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
||||||
|
|
||||||
let onPictureInPictureStart = EventDispatcher()
|
let onPictureInPictureStart = EventDispatcher()
|
||||||
let onPictureInPictureStop = EventDispatcher()
|
let onPictureInPictureStop = EventDispatcher()
|
||||||
+ let onEnterFullscreen = EventDispatcher()
|
+ let onEnterFullscreen = EventDispatcher()
|
||||||
+ let onExitFullscreen = EventDispatcher()
|
+ let onExitFullscreen = EventDispatcher()
|
||||||
|
|
||||||
public override var bounds: CGRect {
|
public override var bounds: CGRect {
|
||||||
didSet {
|
didSet {
|
||||||
@@ -163,6 +165,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
@@ -163,6 +165,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
||||||
|
@ -112,7 +346,7 @@ index f4579e4..10c5908 100644
|
||||||
+ onEnterFullscreen()
|
+ onEnterFullscreen()
|
||||||
isFullscreen = true
|
isFullscreen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +182,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
@@ -179,6 +182,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
||||||
if wasPlaying {
|
if wasPlaying {
|
||||||
self.player?.pointer.play()
|
self.player?.pointer.play()
|
||||||
|
@ -121,6 +355,19 @@ index f4579e4..10c5908 100644
|
||||||
self.isFullscreen = false
|
self.isFullscreen = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
diff --git a/node_modules/expo-video/src/VideoPlayer.types.ts b/node_modules/expo-video/src/VideoPlayer.types.ts
|
||||||
|
index aaf4b63..f438196 100644
|
||||||
|
--- a/node_modules/expo-video/src/VideoPlayer.types.ts
|
||||||
|
+++ b/node_modules/expo-video/src/VideoPlayer.types.ts
|
||||||
|
@@ -151,6 +151,8 @@ export type VideoPlayerEvents = {
|
||||||
|
* Handler for an event emitted when the current media source of the player changes.
|
||||||
|
*/
|
||||||
|
sourceChange(newSource: VideoSource, previousSource: VideoSource): void;
|
||||||
|
+
|
||||||
|
+ timeRemainingChange(timeRemaining: number): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
diff --git a/node_modules/expo-video/src/VideoView.types.ts b/node_modules/expo-video/src/VideoView.types.ts
|
diff --git a/node_modules/expo-video/src/VideoView.types.ts b/node_modules/expo-video/src/VideoView.types.ts
|
||||||
index 29fe5db..e1fbf59 100644
|
index 29fe5db..e1fbf59 100644
|
||||||
--- a/node_modules/expo-video/src/VideoView.types.ts
|
--- a/node_modules/expo-video/src/VideoView.types.ts
|
||||||
|
|
|
@ -102,29 +102,22 @@ function VideoControls({
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [isMuted, setIsMuted] = useState(player.muted)
|
const [isMuted, setIsMuted] = useState(player.muted)
|
||||||
const [duration, setDuration] = useState(() => Math.floor(player.duration))
|
const [timeRemaining, setTimeRemaining] = React.useState(0)
|
||||||
const [currentTime, setCurrentTime] = useState(() =>
|
|
||||||
Math.floor(player.currentTime),
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
|
||||||
// duration gets reset to 0 on loop
|
|
||||||
if (player.duration) setDuration(Math.floor(player.duration))
|
|
||||||
setCurrentTime(Math.floor(player.currentTime))
|
|
||||||
|
|
||||||
// how often should we update the time?
|
|
||||||
// 1000 gets out of sync with the video time
|
|
||||||
}, 250)
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
const sub = player.addListener('volumeChange', ({isMuted}) => {
|
const volumeSub = player.addListener('volumeChange', ({isMuted}) => {
|
||||||
setIsMuted(isMuted)
|
setIsMuted(isMuted)
|
||||||
})
|
})
|
||||||
|
const timeSub = player.addListener(
|
||||||
|
'timeRemainingChange',
|
||||||
|
secondsRemaining => {
|
||||||
|
setTimeRemaining(secondsRemaining)
|
||||||
|
},
|
||||||
|
)
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval)
|
volumeSub.remove()
|
||||||
sub.remove()
|
timeSub.remove()
|
||||||
}
|
}
|
||||||
}, [player])
|
}, [player])
|
||||||
|
|
||||||
|
@ -160,8 +153,7 @@ function VideoControls({
|
||||||
// 1. timeRemaining is a number - was seeing NaNs
|
// 1. timeRemaining is a number - was seeing NaNs
|
||||||
// 2. duration is greater than 0 - means metadata has loaded
|
// 2. duration is greater than 0 - means metadata has loaded
|
||||||
// 3. we're less than 5 second into the video
|
// 3. we're less than 5 second into the video
|
||||||
const timeRemaining = duration - currentTime
|
const showTime = !isNaN(timeRemaining)
|
||||||
const showTime = !isNaN(timeRemaining) && duration > 0 && currentTime <= 5
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.absolute, a.inset_0]}>
|
<View style={[a.absolute, a.inset_0]}>
|
||||||
|
@ -173,35 +165,33 @@ function VideoControls({
|
||||||
accessibilityHint={_(msg`Tap to enter full screen`)}
|
accessibilityHint={_(msg`Tap to enter full screen`)}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
/>
|
/>
|
||||||
{duration > 0 && (
|
<Animated.View
|
||||||
<Animated.View
|
entering={FadeInDown.duration(300)}
|
||||||
entering={FadeInDown.duration(300)}
|
style={{
|
||||||
style={{
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
borderRadius: 6,
|
||||||
borderRadius: 6,
|
paddingHorizontal: 6,
|
||||||
paddingHorizontal: 6,
|
paddingVertical: 3,
|
||||||
paddingVertical: 3,
|
position: 'absolute',
|
||||||
position: 'absolute',
|
bottom: 5,
|
||||||
bottom: 5,
|
right: 5,
|
||||||
right: 5,
|
minHeight: 20,
|
||||||
minHeight: 20,
|
justifyContent: 'center',
|
||||||
justifyContent: 'center',
|
}}>
|
||||||
}}>
|
<Pressable
|
||||||
<Pressable
|
onPress={toggleMuted}
|
||||||
onPress={toggleMuted}
|
style={a.flex_1}
|
||||||
style={a.flex_1}
|
accessibilityLabel={isMuted ? _(msg`Muted`) : _(msg`Unmuted`)}
|
||||||
accessibilityLabel={isMuted ? _(msg`Muted`) : _(msg`Unmuted`)}
|
accessibilityHint={_(msg`Tap to toggle sound`)}
|
||||||
accessibilityHint={_(msg`Tap to toggle sound`)}
|
accessibilityRole="button"
|
||||||
accessibilityRole="button"
|
hitSlop={HITSLOP_30}>
|
||||||
hitSlop={HITSLOP_30}>
|
{isMuted ? (
|
||||||
{isMuted ? (
|
<MuteIcon width={14} fill={t.palette.white} />
|
||||||
<MuteIcon width={14} fill={t.palette.white} />
|
) : (
|
||||||
) : (
|
<UnmuteIcon width={14} fill={t.palette.white} />
|
||||||
<UnmuteIcon width={14} fill={t.palette.white} />
|
)}
|
||||||
)}
|
</Pressable>
|
||||||
</Pressable>
|
</Animated.View>
|
||||||
</Animated.View>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue