diff --git a/packages/react-native-video/android/src/main/AndroidManifest.xml b/packages/react-native-video/android/src/main/AndroidManifest.xml
index bb10a3f9..f79cac1a 100644
--- a/packages/react-native-video/android/src/main/AndroidManifest.xml
+++ b/packages/react-native-video/android/src/main/AndroidManifest.xml
@@ -1,10 +1,3 @@
-
-
-
-
diff --git a/packages/react-native-video/android/src/main/AndroidManifestNew.xml b/packages/react-native-video/android/src/main/AndroidManifestNew.xml
index bb10a3f9..f79cac1a 100644
--- a/packages/react-native-video/android/src/main/AndroidManifestNew.xml
+++ b/packages/react-native-video/android/src/main/AndroidManifestNew.xml
@@ -1,10 +1,3 @@
-
-
-
-
diff --git a/packages/react-native-video/android/src/main/java/com/video/core/VideoManager.kt b/packages/react-native-video/android/src/main/java/com/video/core/VideoManager.kt
index 0c4acb37..979f41a4 100644
--- a/packages/react-native-video/android/src/main/java/com/video/core/VideoManager.kt
+++ b/packages/react-native-video/android/src/main/java/com/video/core/VideoManager.kt
@@ -3,7 +3,6 @@ package com.video.core
import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi
import com.margelo.nitro.video.HybridVideoPlayer
-import com.video.core.activities.FullscreenVideoViewActivity
import com.video.view.VideoView
import java.lang.ref.WeakReference
@@ -13,16 +12,8 @@ object VideoManager {
private val views = mutableMapOf>()
// player -> list of nitroIds of views that are using this player
private val players = mutableMapOf>()
- // fullscreen activity id (hash code) -> weak FullscreenVideoViewActivity
- private val fullscreenActivities = mutableMapOf>()
fun maybePassPlayerToView(player: HybridVideoPlayer) {
- // If we have fullscreen activity open, we don't want to move player from it
- // Fullscreen activity will attach player to view after destroy
- if (fullscreenActivities.isNotEmpty()) {
- return
- }
-
val views = players[player]?.mapNotNull { getVideoViewWeakReferenceByNitroId(it)?.get() } ?: return
val latestView = views.lastOrNull() ?: return
@@ -52,72 +43,54 @@ object VideoManager {
players[player]?.add(view.nitroId)
}
- fun removeViewFromPlayer(view: VideoView, player: HybridVideoPlayer, moveToLatestView: Boolean = true) {
+ fun removeViewFromPlayer(view: VideoView, player: HybridVideoPlayer) {
players[player]?.remove(view.nitroId)
- if(moveToLatestView) maybePassPlayerToView(player)
+
+ // If this was the last view using this player, clean up
+ if (players[player]?.isEmpty() == true) {
+ players.remove(player)
+ } else {
+ // If there are other views using this player, move to the latest one
+ maybePassPlayerToView(player)
+ }
}
fun registerPlayer(player: HybridVideoPlayer) {
- players[player] = players.getOrDefault(player, mutableListOf())
+ if (!players.containsKey(player)) {
+ players[player] = mutableListOf()
+ }
}
fun unregisterPlayer(player: HybridVideoPlayer) {
- // clear player from all views
- val views = players[player]?.mapNotNull { getVideoViewWeakReferenceByNitroId(it)?.get() } ?: return
-
- views.forEach { view ->
- // We are destroying player, so we don't need to look for a new view
- removeViewFromPlayer(view, player, moveToLatestView = false)
- }
-
- // Clear player from views
- views.forEach {
- it.hybridPlayer = null
- }
-
players.remove(player)
}
- fun registerFullscreenActivity(activity: FullscreenVideoViewActivity, id: Int) {
- fullscreenActivities[id] = WeakReference(activity)
+ fun getPlayerByNitroId(nitroId: Int): HybridVideoPlayer? {
+ return players.keys.find { player ->
+ players[player]?.contains(nitroId) == true
+ }
}
- fun unregisterFullscreenActivity(id: Int, player: HybridVideoPlayer?, moveToLatestView: Boolean = true) {
- fullscreenActivities.remove(id)
-
- if (player != null && moveToLatestView) {
- maybePassPlayerToView(player)
+ fun updateVideoViewNitroId(oldNitroId: Int, newNitroId: Int, view: VideoView) {
+ // Remove old mapping
+ if (oldNitroId != -1) {
+ views.remove(oldNitroId)
+
+ // Update player mappings
+ players.keys.forEach { player ->
+ players[player]?.let { nitroIds ->
+ if (nitroIds.remove(oldNitroId)) {
+ nitroIds.add(newNitroId)
}
+ }
+ }
+ }
+
+ // Add new mapping
+ views[newNitroId] = WeakReference(view)
}
fun getVideoViewWeakReferenceByNitroId(nitroId: Int): WeakReference? {
return views[nitroId]
}
-
- fun updateVideoViewNitroId(oldNitroId: Int, newNitroId: Int, view: VideoView) {
- // Update view in views map
- views.remove(oldNitroId)
- views[newNitroId] = WeakReference(view)
-
- // Update view in players map
- players.forEach { (_, nitroIds) ->
- // replace old id with new id (keep order)
- val index = nitroIds.indexOf(oldNitroId)
-
- if (index != -1) {
- nitroIds[index] = newNitroId
- }
- }
-
- // Update view in fullscreen activities map
- fullscreenActivities.forEach { (_, activity) ->
- if (activity.get()?.videoViewNitroId == oldNitroId) {
- activity.get()?.videoViewNitroId = newNitroId
- }
- }
- }
-
- fun getPlayerByNitroId(nitroId: Int): HybridVideoPlayer? {
- return players.entries.firstOrNull { it.value.contains(nitroId) }?.key
- }
}
diff --git a/packages/react-native-video/android/src/main/java/com/video/core/activities/FullscreenVideoViewActivity.kt b/packages/react-native-video/android/src/main/java/com/video/core/activities/FullscreenVideoViewActivity.kt
deleted file mode 100644
index 6c0ec012..00000000
--- a/packages/react-native-video/android/src/main/java/com/video/core/activities/FullscreenVideoViewActivity.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.video.core.activities
-
-import android.annotation.SuppressLint
-import android.app.Activity
-import android.app.PictureInPictureParams
-import android.content.res.Configuration
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.util.Rational
-import android.view.View
-import android.view.WindowInsets
-import android.view.WindowInsetsController
-import android.widget.ImageButton
-import androidx.annotation.RequiresApi
-import androidx.media3.common.util.UnstableApi
-import androidx.media3.ui.PlayerView
-import com.margelo.nitro.video.HybridVideoPlayer
-import com.video.R
-import com.video.core.VideoManager
-import com.video.core.utils.PictureInPictureUtils.calculateAspectRatio
-import com.video.core.utils.PictureInPictureUtils.calculateSourceRectHint
-import com.video.view.VideoView
-import java.lang.ref.WeakReference
-
-@UnstableApi
-class FullscreenVideoViewActivity : Activity() {
- private lateinit var container: View
- lateinit var playerView: PlayerView
-
- var videoViewNitroId: Int = -1
- private var videoView: WeakReference? = null
-
- private lateinit var player: HybridVideoPlayer
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.fullscreen_video_view_activity)
- container = findViewById(R.id.fullscreen_container)
- playerView = findViewById(R.id.player_view)
-
- try {
- videoViewNitroId = intent.getIntExtra("nitroId", -1)
-
- if (videoViewNitroId == -1) throw Exception("nitroId not found")
-
- videoView = VideoManager.getVideoViewWeakReferenceByNitroId(videoViewNitroId)
-
- player = VideoManager.getPlayerByNitroId(videoViewNitroId)
- ?: throw Exception("Player not found")
-
- player.moveToFullscreenActivity(this)
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- val params = PictureInPictureParams.Builder()
- .setAutoEnterEnabled(videoView?.get()?.autoEnterPictureInPicture == true)
- .setSourceRectHint(calculateSourceRectHint(playerView))
- .setAspectRatio(calculateAspectRatio(playerView))
- .build()
-
- setPictureInPictureParams(params)
- }
- } catch (error: Error) {
- Log.e("ReactNativeVideo - FullscreenVideoViewActivity", error.message, error)
- finish()
- return
- }
- }
-
- override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
- super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
-
- if (isInPictureInPictureMode) {
- playerView.useController = false
- } else {
- playerView.useController = videoView?.get()?.useController == true
- }
- }
-
- override fun onPostCreate(savedInstanceState: Bundle?) {
- super.onPostCreate(savedInstanceState)
- setupFullScreenButton()
- playerView.setShowSubtitleButton(true)
- hideSystemUI()
- }
-
- override fun onDestroy() {
- super.onDestroy()
- finish()
- }
-
- override fun finish() {
- super.finish()
- VideoManager.unregisterFullscreenActivity(hashCode(), player)
- videoView?.get()?.exitFullscreen()
- }
-
- @SuppressLint("PrivateResource")
- private fun setupFullScreenButton() {
- playerView.setFullscreenButtonClickListener { _ ->
- finish()
- }
-
- // We need to manually change icon, as we are using separate PlayerView in fullscreen activity
- val button = playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
- button.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_exit)
- }
-
- @Suppress("DEPRECATION")
- private fun hideSystemUI() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- container.fitsSystemWindows = false
- container.windowInsetsController?.let { controller ->
- controller.hide(WindowInsets.Type.systemBars())
- controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
- }
- } else {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN)
- }
- }
-}
diff --git a/packages/react-native-video/android/src/main/java/com/video/core/fragments/FullscreenVideoFragment.kt b/packages/react-native-video/android/src/main/java/com/video/core/fragments/FullscreenVideoFragment.kt
new file mode 100644
index 00000000..35d4eb2f
--- /dev/null
+++ b/packages/react-native-video/android/src/main/java/com/video/core/fragments/FullscreenVideoFragment.kt
@@ -0,0 +1,231 @@
+package com.video.core.fragments
+
+import android.annotation.SuppressLint
+import android.content.res.Configuration
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import androidx.activity.OnBackPressedCallback
+import androidx.annotation.OptIn
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.media3.common.util.UnstableApi
+import com.video.core.utils.PictureInPictureUtils.createPictureInPictureParams
+import com.video.view.VideoView
+import java.util.UUID
+
+@OptIn(UnstableApi::class)
+class FullscreenVideoFragment(private val videoView: VideoView) : Fragment() {
+ val id: String = UUID.randomUUID().toString()
+
+ private var container: FrameLayout? = null
+ private var originalPlayerParent: ViewGroup? = null
+ private var originalPlayerLayoutParams: ViewGroup.LayoutParams? = null
+ private var rootContentViews: List = listOf()
+
+ // Back press callback to handle back navigation
+ private val backPressCallback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ videoView.exitFullscreen()
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Create a fullscreen container
+ this.container = FrameLayout(requireContext()).apply {
+ layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ setBackgroundColor(android.graphics.Color.BLACK)
+ keepScreenOn = true
+ }
+ return this.container
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ // Register back press callback
+ requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressCallback)
+
+ enterFullscreenMode()
+ setupPlayerView()
+ hideSystemUI()
+
+ // Update PiP params if supported
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ try {
+ val params = createPictureInPictureParams(videoView)
+ requireActivity().setPictureInPictureParams(params)
+ } catch (_: Exception) {}
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+
+ // Handle PiP mode changes
+ val isInPictureInPictureMode =
+ requireActivity().isInPictureInPictureMode
+
+ if (isInPictureInPictureMode) {
+ videoView.playerView.useController = false
+ } else {
+ videoView.playerView.useController = videoView.useController
+ }
+ }
+
+ private fun enterFullscreenMode() {
+ // Store original parent and layout params
+ originalPlayerParent = videoView.playerView.parent as? ViewGroup
+ originalPlayerLayoutParams = videoView.playerView.layoutParams
+
+ // Remove player from original parent
+ originalPlayerParent?.removeView(videoView.playerView)
+
+ // Hide all root content views
+ val currentActivity = requireActivity()
+ val rootContent = currentActivity.window.decorView.findViewById(android.R.id.content)
+ rootContentViews = (0 until rootContent.childCount)
+ .map { rootContent.getChildAt(it) }
+ .filter { it.isVisible }
+
+ rootContentViews.forEach { view ->
+ view.visibility = View.GONE
+ }
+
+ // Add our fullscreen container to root
+ rootContent.addView(container,
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+ }
+
+ private fun setupPlayerView() {
+ // Add PlayerView to our container
+ container?.addView(videoView.playerView,
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ )
+
+ videoView.playerView.setBackgroundColor(android.graphics.Color.BLACK)
+ videoView.playerView.setShutterBackgroundColor(android.graphics.Color.BLACK)
+
+ // We need show controls in fullscreen
+ videoView.playerView.useController = true
+
+ setupFullscreenButton()
+ videoView.playerView.setShowSubtitleButton(true)
+ }
+
+ @SuppressLint("PrivateResource")
+ private fun setupFullscreenButton() {
+ videoView.playerView.setFullscreenButtonClickListener { _ ->
+ videoView.exitFullscreen()
+ }
+
+ // Change icon to exit fullscreen
+ val button = videoView.playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
+ button?.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_exit)
+ }
+
+ @Suppress("DEPRECATION")
+ private fun hideSystemUI() {
+ val currentActivity = requireActivity()
+ container?.let { container ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ container.fitsSystemWindows = false
+ container.windowInsetsController?.let { controller ->
+ controller.hide(WindowInsets.Type.systemBars())
+ controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ }
+ } else {
+ currentActivity.window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_FULLSCREEN
+ )
+ }
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ private fun restoreSystemUI() {
+ val currentActivity = requireActivity()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ container?.windowInsetsController?.show(WindowInsets.Type.systemBars())
+ } else {
+ currentActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
+ }
+ }
+
+ fun exitFullscreen() {
+ // Remove back press callback since we're exiting
+ backPressCallback.remove()
+
+ restoreSystemUI()
+
+ if (videoView.useController == false) {
+ videoView.playerView.useController = false
+ }
+
+ // Ensure PlayerView keeps black background when returning to normal mode
+ videoView.playerView.setBackgroundColor(android.graphics.Color.BLACK)
+ videoView.playerView.setShutterBackgroundColor(android.graphics.Color.BLACK)
+
+ // Remove PlayerView from our container
+ container?.removeView(videoView.playerView)
+
+ // Remove our container from root
+ val currentActivity = requireActivity()
+ val rootContent = currentActivity.window.decorView.findViewById(android.R.id.content)
+ rootContent.removeView(container)
+
+ // Restore root content views
+ rootContentViews.forEach { it.visibility = View.VISIBLE }
+ rootContentViews = listOf()
+
+ // Safely restore PlayerView to original parent
+ // First, ensure PlayerView is removed from any current parent
+ val currentParent = videoView.playerView.parent as? ViewGroup
+ currentParent?.removeView(videoView.playerView)
+
+ // Now add it back to the original parent
+ originalPlayerParent?.addView(videoView.playerView, originalPlayerLayoutParams)
+
+ // Remove this fragment
+ parentFragmentManager.beginTransaction()
+ .remove(this)
+ .commitAllowingStateLoss()
+
+ // Notify VideoView that we've exited fullscreen
+ videoView.isInFullscreen = false
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+
+ // Ensure we clean up properly if fragment is destroyed
+ if (videoView.isInFullscreen) {
+ exitFullscreen()
+ }
+ }
+}
diff --git a/packages/react-native-video/android/src/main/java/com/video/hybrids/videoplayer/HybridVideoPlayer.kt b/packages/react-native-video/android/src/main/java/com/video/hybrids/videoplayer/HybridVideoPlayer.kt
index cc590df5..cb346a74 100644
--- a/packages/react-native-video/android/src/main/java/com/video/hybrids/videoplayer/HybridVideoPlayer.kt
+++ b/packages/react-native-video/android/src/main/java/com/video/hybrids/videoplayer/HybridVideoPlayer.kt
@@ -28,7 +28,6 @@ import com.margelo.nitro.core.Promise
import com.video.core.LibraryError
import com.video.core.PlayerError
import com.video.core.VideoManager
-import com.video.core.activities.FullscreenVideoViewActivity
import com.video.core.player.OnAudioFocusChangedListener
import com.video.core.recivers.AudioBecomingNoisyReceiver
import com.video.core.utils.Threading.runOnMainThread
@@ -295,15 +294,6 @@ class HybridVideoPlayer() : HybridVideoPlayerSpec() {
}
}
- fun moveToFullscreenActivity(activity: FullscreenVideoViewActivity) {
- VideoManager.registerFullscreenActivity(activity, activity.hashCode())
-
- runOnMainThreadSync {
- PlayerView.switchTargetView(playerPointer, currentPlayerView?.get(), activity.playerView)
- currentPlayerView = WeakReference(activity.playerView)
- }
- }
-
override val memorySize: Long
get() = allocator?.totalBytesAllocated?.toLong() ?: 0L
diff --git a/packages/react-native-video/android/src/main/java/com/video/hybrids/videoviewviewmanager/HybridVideoViewViewManager.kt b/packages/react-native-video/android/src/main/java/com/video/hybrids/videoviewviewmanager/HybridVideoViewViewManager.kt
index 96a0cfd3..3be49a1d 100644
--- a/packages/react-native-video/android/src/main/java/com/video/hybrids/videoviewviewmanager/HybridVideoViewViewManager.kt
+++ b/packages/react-native-video/android/src/main/java/com/video/hybrids/videoviewviewmanager/HybridVideoViewViewManager.kt
@@ -35,7 +35,7 @@ class HybridVideoViewViewManager(nitroId: Int): HybridVideoViewViewManagerSpec()
}
override fun exitFullscreen() {
- throw LibraryError.MethodNotSupported("exitFullscreen")
+ videoView.get()?.exitFullscreen()
}
override fun enterPictureInPicture() {
diff --git a/packages/react-native-video/android/src/main/java/com/video/view/VideoView.kt b/packages/react-native-video/android/src/main/java/com/video/view/VideoView.kt
index 7223bdce..e5807ef9 100644
--- a/packages/react-native-video/android/src/main/java/com/video/view/VideoView.kt
+++ b/packages/react-native-video/android/src/main/java/com/video/view/VideoView.kt
@@ -2,7 +2,6 @@ package com.video.view
import android.annotation.SuppressLint
import android.content.Context
-import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.util.AttributeSet
@@ -22,7 +21,7 @@ import com.margelo.nitro.video.VideoViewEvents
import com.video.core.LibraryError
import com.video.core.VideoManager
import com.video.core.VideoViewError
-import com.video.core.activities.FullscreenVideoViewActivity
+import com.video.core.fragments.FullscreenVideoFragment
import com.video.core.fragments.PictureInPictureHelperFragment
import com.video.core.utils.PictureInPictureUtils.canEnterPictureInPicture
import com.video.core.utils.PictureInPictureUtils.createPictureInPictureParams
@@ -108,6 +107,7 @@ class VideoView @JvmOverloads constructor(
}
private var rootContentViews: List = listOf()
private var pictureInPictureHelperTag: String? = null
+ private var fullscreenFragmentTag: String? = null
val applicationContent: ReactApplicationContext
get() {
@@ -136,10 +136,14 @@ class VideoView @JvmOverloads constructor(
post(layoutRunnable)
}
+ @SuppressLint("PrivateResource")
private fun setupFullscreenButton() {
playerView.setFullscreenButtonClickListener { _ ->
enterFullscreen()
}
+
+ playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
+ ?.setImageResource(androidx.media3.ui.R.drawable.exo_ic_fullscreen_enter)
}
fun enterFullscreen() {
@@ -147,25 +151,54 @@ class VideoView @JvmOverloads constructor(
return
}
- isInFullscreen = true
-
- val intent = Intent(context, FullscreenVideoViewActivity::class.java)
- intent.putExtra("nitroId", nitroId)
+ val currentActivity = applicationContent.currentActivity
+ if (currentActivity !is FragmentActivity) {
+ Log.e("ReactNativeVideo", "Current activity is not a FragmentActivity, cannot enter fullscreen")
+ return
+ }
try {
- val currentActivity = applicationContent.currentActivity
- currentActivity?.startActivity(intent)
+ events.willEnterFullscreen?.let { it() }
+
+ val fragment = FullscreenVideoFragment(this)
+ fullscreenFragmentTag = fragment.id
+
+ currentActivity.supportFragmentManager.beginTransaction()
+ .add(fragment, fragment.id)
+ .commitAllowingStateLoss()
+
+ isInFullscreen = true
} catch (err: Exception) {
- val debugMessage = "Failed to start fullscreen activity for nitroId: $nitroId"
+ val debugMessage = "Failed to start fullscreen fragment for nitroId: $nitroId"
Log.e("ReactNativeVideo", debugMessage, err)
}
}
@SuppressLint("PrivateResource")
fun exitFullscreen() {
- // Change fullscreen button icon back to enter fullscreen
- playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
- ?.setImageResource(androidx.media3.ui.R.drawable.exo_ic_fullscreen_enter)
+ if (!isInFullscreen) {
+ return
+ }
+
+ events.willExitFullscreen?.let { it() }
+
+ val currentActivity = applicationContent.currentActivity
+ fullscreenFragmentTag?.let { tag ->
+ (currentActivity as? FragmentActivity)?.let { activity ->
+ activity.supportFragmentManager.findFragmentByTag(tag)?.let { fragment ->
+ // The fragment will handle its own removal in exitFullscreen()
+ if (fragment is FullscreenVideoFragment) {
+ runOnMainThread {
+ fragment.exitFullscreen()
+ }
+ }
+ }
+ }
+ fullscreenFragmentTag = null
+ }
+
+ // Change fullscreen button icon back to enter fullscreen and update callback
+ setupFullscreenButton()
isInFullscreen = false
}
@@ -199,11 +232,28 @@ class VideoView @JvmOverloads constructor(
}
}
+ private fun removeFullscreenFragment() {
+ val currentActivity = applicationContent.currentActivity
+ fullscreenFragmentTag?.let { tag ->
+ (currentActivity as? FragmentActivity)?.let { activity ->
+ activity.supportFragmentManager.findFragmentByTag(tag)?.let { fragment ->
+ activity.supportFragmentManager.beginTransaction()
+ .remove(fragment)
+ .commitAllowingStateLoss()
+ }
+ }
+ fullscreenFragmentTag = null
+ }
+ }
+
fun hideRootContentViews() {
// Remove playerView from parent
// In PiP mode, we don't want to show the controller
// Controls are handled by System if we have MediaSession
playerView.useController = false
+ playerView.setBackgroundColor(Color.BLACK)
+ playerView.setShutterBackgroundColor(Color.BLACK)
+
(playerView.parent as? ViewGroup)?.removeView(playerView)
val currentActivity = applicationContent.currentActivity ?: return
@@ -225,6 +275,9 @@ class VideoView @JvmOverloads constructor(
// Reset PlayerView settings
playerView.useController = useController
+ playerView.setBackgroundColor(Color.BLACK)
+ playerView.setShutterBackgroundColor(Color.BLACK)
+
val currentActivity = applicationContent.currentActivity ?: return
val rootContent = currentActivity.window.decorView.findViewById(android.R.id.content)
rootContent.removeView(playerView)
@@ -273,6 +326,7 @@ class VideoView @JvmOverloads constructor(
// -------- View Lifecycle Methods --------
override fun onDetachedFromWindow() {
removePipHelper()
+ removeFullscreenFragment()
VideoManager.unregisterView(this)
super.onDetachedFromWindow()
}
diff --git a/packages/react-native-video/android/src/main/res/layout/fullscreen_video_view_activity.xml b/packages/react-native-video/android/src/main/res/layout/fullscreen_video_view_activity.xml
deleted file mode 100644
index 8e5cdd47..00000000
--- a/packages/react-native-video/android/src/main/res/layout/fullscreen_video_view_activity.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-