about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-09-03 08:41:14 -0700
committerGitHub <noreply@github.com>2024-09-03 08:41:14 -0700
commitdde72b48e12de0b87da60000b7705e475c795429 (patch)
treee37223209bc573a422b350bcb44cd8daea3dd6b1
parent05e61346b8c20b242edf748d2d84a66aefa390f6 (diff)
downloadvoidsky-dde72b48e12de0b87da60000b7705e475c795429.tar.zst
[Video] Manage foreground/background playback on the native side (#5104)
-rw-r--r--patches/expo-video+1.2.4.patch93
-rw-r--r--patches/expo-video+1.2.4.patch.md11
-rw-r--r--src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx29
3 files changed, 104 insertions, 29 deletions
diff --git a/patches/expo-video+1.2.4.patch b/patches/expo-video+1.2.4.patch
index f9e91a439..13fe25eda 100644
--- a/patches/expo-video+1.2.4.patch
+++ b/patches/expo-video+1.2.4.patch
@@ -80,6 +80,57 @@ index 0000000..0249e23
 +  }
 +}
 \ No newline at end of file
+diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
+index 4b6c6d8..e20f51a 100644
+--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
++++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
+@@ -1,5 +1,6 @@
+ package expo.modules.video
+
++import android.provider.MediaStore.Video
+ import androidx.annotation.OptIn
+ import androidx.media3.common.util.UnstableApi
+ import expo.modules.kotlin.AppContext
+@@ -15,6 +16,8 @@ object VideoManager {
+   // Keeps track of all existing VideoPlayers, and whether they are attached to a VideoView
+   private var videoPlayersToVideoViews = mutableMapOf<VideoPlayer, MutableList<VideoView>>()
+
++  private var previouslyPlayingViews: MutableList<VideoView>? = null
++
+   private lateinit var audioFocusManager: AudioFocusManager
+
+   fun onModuleCreated(appContext: AppContext) {
+@@ -69,16 +72,24 @@ object VideoManager {
+     return videoPlayersToVideoViews[videoPlayer]?.isNotEmpty() ?: false
+   }
+
+-  fun onAppForegrounded() = Unit
++  fun onAppForegrounded() {
++    val previouslyPlayingViews = this.previouslyPlayingViews ?: return
++    for (videoView in previouslyPlayingViews) {
++      val player = videoView.videoPlayer?.player ?: continue
++      player.play()
++    }
++    this.previouslyPlayingViews = null
++  }
+
+   fun onAppBackgrounded() {
++    val previouslyPlayingViews = mutableListOf<VideoView>()
+     for (videoView in videoViews.values) {
+-      if (videoView.videoPlayer?.staysActiveInBackground == false &&
+-        !videoView.willEnterPiP &&
+-        !videoView.isInFullscreen
+-      ) {
+-        videoView.videoPlayer?.player?.pause()
++      val player = videoView.videoPlayer?.player ?: continue
++      if (player.isPlaying) {
++        player.pause()
++        previouslyPlayingViews.add(videoView)
+       }
+     }
++    this.previouslyPlayingViews = previouslyPlayingViews
+   }
+ }
 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
 --- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
@@ -220,12 +271,48 @@ index cb9ca6d..ed8bb7e 100644
  //# sourceMappingURL=VideoView.types.d.ts.map
 \ No newline at end of file
 diff --git a/node_modules/expo-video/ios/VideoManager.swift b/node_modules/expo-video/ios/VideoManager.swift
-index 094a8b0..412fd0c 100644
+index 094a8b0..3f00525 100644
 --- a/node_modules/expo-video/ios/VideoManager.swift
 +++ b/node_modules/expo-video/ios/VideoManager.swift
-@@ -51,45 +51,45 @@ class VideoManager {
+@@ -12,6 +12,7 @@ class VideoManager {
+
+   private var videoViews = NSHashTable<VideoView>.weakObjects()
+   private var videoPlayers = NSHashTable<VideoPlayer>.weakObjects()
++  private var previouslyPlayingPlayers: [VideoPlayer]?
+
+   func register(videoPlayer: VideoPlayer) {
+     videoPlayers.add(videoPlayer)
+@@ -33,63 +34,70 @@ class VideoManager {
+     for videoPlayer in videoPlayers.allObjects {
+       videoPlayer.setTracksEnabled(true)
+     }
++
++    if let previouslyPlayingPlayers = self.previouslyPlayingPlayers {
++      previouslyPlayingPlayers.forEach { player in
++        player.pointer.play()
++      }
++    }
+   }
+
+   func onAppBackgrounded() {
++    var previouslyPlayingPlayers: [VideoPlayer] = []
+     for videoView in videoViews.allObjects {
+       guard let player = videoView.player else {
+         continue
+       }
+-      if player.staysActiveInBackground == true {
+-        player.setTracksEnabled(videoView.isInPictureInPicture)
+-      } else if !videoView.isInPictureInPicture {
++      if player.isPlaying {
+         player.pointer.pause()
++        previouslyPlayingPlayers.append(player)
+       }
+     }
++    self.previouslyPlayingPlayers = previouslyPlayingPlayers
+   }
+
    // MARK: - Audio Session Management
- 
+
    internal func setAppropriateAudioSessionOrWarn() {
 -    let audioSession = AVAudioSession.sharedInstance()
 -    var audioSessionCategoryOptions: AVAudioSession.CategoryOptions = []
diff --git a/patches/expo-video+1.2.4.patch.md b/patches/expo-video+1.2.4.patch.md
index 6dd85308b..99c14c286 100644
--- a/patches/expo-video+1.2.4.patch.md
+++ b/patches/expo-video+1.2.4.patch.md
@@ -2,8 +2,17 @@
 
 ## `expo-video` Patch
 
-This patch adds two props to `VideoView`: `onEnterFullscreen` and `onExitFullscreen` which do exactly what they say on
+### `onEnterFullScreen`/`onExitFullScreen`
+Adds two props to `VideoView`: `onEnterFullscreen` and `onExitFullscreen` which do exactly what they say on
 the tin.
 
+### Removing audio session management
+
 This patch also removes the audio session management that Expo does on its own, as we handle audio session management
 ourselves.
+
+### Pausing/playing on background/foreground
+
+Instead of handling the pausing/playing of videos in React, we'll handle them here. There's some logic that we do not
+need (around PIP mode) that we can remove, and just pause any playing players on background and then resume them on
+foreground.
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
index 59f9d9f97..fa59b9c99 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
@@ -5,12 +5,9 @@ import {VideoPlayer, VideoView} from 'expo-video'
 import {AppBskyEmbedVideo} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useIsFocused} from '@react-navigation/native'
 
 import {HITSLOP_30} from '#/lib/constants'
-import {useAppState} from '#/lib/hooks/useAppState'
 import {clamp} from '#/lib/numbers'
-import {logger} from '#/logger'
 import {useActiveVideoNative} from 'view/com/util/post-embeds/ActiveVideoNativeContext'
 import {atoms as a, useTheme} from '#/alf'
 import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
@@ -29,26 +26,6 @@ export function VideoEmbedInnerNative({
   const {_} = useLingui()
   const {player} = useActiveVideoNative()
   const ref = useRef<VideoView>(null)
-  const isScreenFocused = useIsFocused()
-  const isAppFocused = useAppState()
-
-  useEffect(() => {
-    try {
-      if (isAppFocused === 'active' && isScreenFocused && !player.playing) {
-        PlatformInfo.setAudioCategory(AudioCategory.Ambient)
-        PlatformInfo.setAudioActive(false)
-        player.muted = true
-        player.play()
-      } else if (player.playing) {
-        player.pause()
-      }
-    } catch (err) {
-      logger.error(
-        'Failed to play/pause while backgrounding/switching screens',
-        {safeMessage: err},
-      )
-    }
-  }, [isAppFocused, player, isScreenFocused])
 
   const enterFullscreen = useCallback(() => {
     ref.current?.enterFullscreen()
@@ -69,7 +46,7 @@ export function VideoEmbedInnerNative({
         player={player}
         style={[a.flex_1, a.rounded_sm]}
         contentFit="contain"
-        nativeControls={true}
+        nativeControls={false}
         accessibilityIgnoresInvertColors
         onEnterFullscreen={() => {
           PlatformInfo.setAudioCategory(AudioCategory.Playback)
@@ -80,7 +57,9 @@ export function VideoEmbedInnerNative({
           PlatformInfo.setAudioCategory(AudioCategory.Ambient)
           PlatformInfo.setAudioActive(false)
           player.muted = true
-          if (!player.playing) player.play()
+          if (!player.playing) {
+            player.play()
+          }
         }}
         accessibilityLabel={
           embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`)