| ... | ... | @@ -19,6 +19,7 @@ function LoadedMediaVideo(props: { |
| 19 | 19 | }) { |
| 20 | 20 | const focused = useIsFocused(); |
| 21 | 21 | const active = useRef(focused && AppState.currentState === "active"); |
| 22 | const fullscreen = useRef(false); |
| 22 | 23 | const [attempt, setAttempt] = useState(0); |
| 23 | 24 | // Expo's Android player also reports completed playback as idle. |
| 24 | 25 | const [loadState, setLoadState] = useState<"pending" | "complete" | "error">("pending"); |
| ... | ... | @@ -38,10 +39,12 @@ function LoadedMediaVideo(props: { |
| 38 | 39 | |
| 39 | 40 | useEffect(() => { |
| 40 | 41 | active.current = focused && !props.paused && AppState.currentState === "active"; |
| 41 | | if (!active.current) player.pause(); |
| 42 | if (!focused || props.paused || (!active.current && !fullscreen.current)) player.pause(); |
| 43 | // Native background handling distinguishes Android's fullscreen activity |
| 44 | // from leaving the app; React Native reports both as background. |
| 42 | 45 | const subscription = AppState.addEventListener("change", (state) => { |
| 43 | 46 | active.current = focused && !props.paused && state === "active"; |
| 44 | | if (!active.current) player.pause(); |
| 47 | if (state === "inactive" || (state === "background" && !fullscreen.current)) player.pause(); |
| 45 | 48 | }); |
| 46 | 49 | return () => subscription.remove(); |
| 47 | 50 | }, [focused, player, props.paused]); |
| ... | ... | @@ -70,6 +73,12 @@ function LoadedMediaVideo(props: { |
| 70 | 73 | nativeControls |
| 71 | 74 | contentFit="contain" |
| 72 | 75 | fullscreenOptions={{ enable: true }} |
| 76 | onFullscreenEnter={() => { |
| 77 | fullscreen.current = true; |
| 78 | }} |
| 79 | onFullscreenExit={() => { |
| 80 | fullscreen.current = false; |
| 81 | }} |
| 73 | 82 | allowsPictureInPicture={false} |
| 74 | 83 | /> |
| 75 | 84 | {loadState === "error" || (loadState === "complete" && status === "error") ? ( |