From 978683b64582e6363d7b5a1817e22fec342d1c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Piekarz?= <125810604+Pieczasz@users.noreply.github.com> Date: Wed, 2 Jul 2025 08:46:19 +0200 Subject: [PATCH] feat(android): replace custom VideoView with media3 `PlayerView` (#4581) --- .../com/brentvatne/exoplayer/ExoPlayerView.kt | 496 +++++----- .../exoplayer/FullScreenPlayerView.kt | 28 +- .../exoplayer/PictureInPictureUtil.kt | 5 +- .../exoplayer/ReactExoplayerView.java | 673 ++++++++----- .../exoplayer/ReactExoplayerViewManager.kt | 6 - examples/common/BasicExample.tsx | 69 +- .../common/components/AudioTracksSelector.tsx | 8 +- .../common/components/TextTracksSelector.tsx | 14 +- examples/common/constants/general.ts | 40 +- .../ios/ExpoExample.xcodeproj/project.pbxproj | 8 +- examples/expo/ios/Podfile.lock | 933 +++++++++--------- ios/Video/RCTVideo.swift | 24 +- 12 files changed, 1243 insertions(+), 1061 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt index ee24982e..e6296108 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt @@ -1,276 +1,275 @@ package com.brentvatne.exoplayer import android.content.Context -import android.util.Log -import android.util.TypedValue -import android.view.Gravity +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.util.AttributeSet import android.view.SurfaceView import android.view.TextureView import android.view.View -import android.view.ViewGroup +import android.view.View.MeasureSpec import android.widget.FrameLayout -import androidx.core.content.ContextCompat -import androidx.media3.common.AdViewProvider -import androidx.media3.common.C +import android.widget.TextView import androidx.media3.common.Player -import androidx.media3.common.Tracks -import androidx.media3.common.VideoSize -import androidx.media3.common.text.Cue -import androidx.media3.common.util.Assertions +import androidx.media3.common.Timeline import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer -import androidx.media3.ui.SubtitleView +import androidx.media3.ui.AspectRatioFrameLayout +import androidx.media3.ui.DefaultTimeBar +import androidx.media3.ui.PlayerView import com.brentvatne.common.api.ResizeMode import com.brentvatne.common.api.SubtitleStyle import com.brentvatne.common.api.ViewType -import com.brentvatne.common.toolbox.DebugLog @UnstableApi -class ExoPlayerView(private val context: Context) : - FrameLayout(context, null, 0), - AdViewProvider { - - var surfaceView: View? = null - private set - private var shutterView: View - private var subtitleLayout: SubtitleView - private var layout: AspectRatioFrameLayout - private var componentListener: ComponentListener - private var player: ExoPlayer? = null - private var layoutParams: ViewGroup.LayoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - private var adOverlayFrameLayout: FrameLayout? = null - val isPlaying: Boolean - get() = player != null && player?.isPlaying == true - - @ViewType.ViewType - private var viewType = ViewType.VIEW_TYPE_SURFACE - private var hideShutterView = false +class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + FrameLayout(context, attrs, defStyleAttr) { private var localStyle = SubtitleStyle() + private var surfaceView: View? = null + private val liveBadge: TextView = TextView(context).apply { + text = "LIVE" + setTextColor(Color.WHITE) + textSize = 12f + val drawable = GradientDrawable() + drawable.setColor(Color.RED) + drawable.cornerRadius = 6f + background = drawable + setPadding(12, 4, 12, 4) + visibility = View.GONE + } + + private val playerView = PlayerView(context).apply { + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + setShutterBackgroundColor(Color.TRANSPARENT) + useController = true + controllerAutoShow = true + controllerHideOnTouch = true + controllerShowTimeoutMs = 5000 + // Don't show subtitle button by default - will be enabled when tracks are available + setShowSubtitleButton(false) + // Enable proper surface view handling to prevent rendering issues + setUseArtwork(false) + setDefaultArtwork(null) + // Ensure proper video scaling + resizeMode = androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT + } init { - componentListener = ComponentListener() - - val aspectRatioParams = LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT - ) - aspectRatioParams.gravity = Gravity.CENTER - layout = AspectRatioFrameLayout(context) - layout.layoutParams = aspectRatioParams - - shutterView = View(context) - shutterView.layoutParams = layoutParams - shutterView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black)) - - subtitleLayout = SubtitleView(context) - subtitleLayout.layoutParams = layoutParams - subtitleLayout.setUserDefaultStyle() - subtitleLayout.setUserDefaultTextSize() - - updateSurfaceView(viewType) - - layout.addView(shutterView, 1, layoutParams) - if (localStyle.subtitlesFollowVideo) { - layout.addView(subtitleLayout, layoutParams) - } - - addViewInLayout(layout, 0, aspectRatioParams) - if (!localStyle.subtitlesFollowVideo) { - addViewInLayout(subtitleLayout, 1, layoutParams) - } + addView(playerView) + val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) + lp.setMargins(16, 16, 16, 16) + addView(liveBadge, lp) } - private fun clearVideoView() { - when (val view = surfaceView) { - is TextureView -> player?.clearVideoTextureView(view) + fun setPlayer(player: ExoPlayer?) { + val currentPlayer = playerView.player - is SurfaceView -> player?.clearVideoSurfaceView(view) + if (currentPlayer != null) { + currentPlayer.removeListener(playerListener) + // Clear any existing surface from the player + when (surfaceView) { + is SurfaceView -> currentPlayer.clearVideoSurfaceView(surfaceView as SurfaceView) + is TextureView -> currentPlayer.clearVideoTextureView(surfaceView as TextureView) + } + } - else -> { - Log.w( - "clearVideoView", - "Unexpected surfaceView type: ${surfaceView?.javaClass?.name}" - ) + playerView.player = player + + if (player != null) { + player.addListener(playerListener) + // Set the surface view for the new player + when (surfaceView) { + is SurfaceView -> player.setVideoSurfaceView(surfaceView as SurfaceView) + is TextureView -> player.setVideoTextureView(surfaceView as TextureView) } } } - private fun setVideoView() { - when (val view = surfaceView) { - is TextureView -> player?.setVideoTextureView(view) + fun getPlayerView(): PlayerView = playerView - is SurfaceView -> player?.setVideoSurfaceView(view) - - else -> { - Log.w( - "setVideoView", - "Unexpected surfaceView type: ${surfaceView?.javaClass?.name}" - ) - } + fun setResizeMode(@ResizeMode.Mode resizeMode: Int) { + playerView.resizeMode = when (resizeMode) { + ResizeMode.RESIZE_MODE_FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL + ResizeMode.RESIZE_MODE_CENTER_CROP -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM + ResizeMode.RESIZE_MODE_FIT -> AspectRatioFrameLayout.RESIZE_MODE_FIT + ResizeMode.RESIZE_MODE_FIXED_WIDTH -> AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH + ResizeMode.RESIZE_MODE_FIXED_HEIGHT -> AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT + else -> AspectRatioFrameLayout.RESIZE_MODE_FIT } } fun setSubtitleStyle(style: SubtitleStyle) { - // ensure we reset subtitle style before reapplying it - subtitleLayout.setUserDefaultStyle() - subtitleLayout.setUserDefaultTextSize() + playerView.subtitleView?.let { subtitleView -> + // Reset to defaults + subtitleView.setUserDefaultStyle() + subtitleView.setUserDefaultTextSize() - if (style.fontSize > 0) { - subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.fontSize.toFloat()) - } - subtitleLayout.setPadding( - style.paddingLeft, - style.paddingTop, - style.paddingTop, - style.paddingBottom - ) - if (style.opacity != 0.0f) { - subtitleLayout.alpha = style.opacity - subtitleLayout.visibility = View.VISIBLE - } else { - subtitleLayout.visibility = View.GONE - } - if (localStyle.subtitlesFollowVideo != style.subtitlesFollowVideo) { - // No need to manipulate layout if value didn't change - if (style.subtitlesFollowVideo) { - removeViewInLayout(subtitleLayout) - layout.addView(subtitleLayout, layoutParams) - } else { - layout.removeViewInLayout(subtitleLayout) - addViewInLayout(subtitleLayout, 1, layoutParams, false) + // Apply custom styling + if (style.fontSize > 0) { + subtitleView.setFixedTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, style.fontSize.toFloat()) + } + + subtitleView.setPadding( + style.paddingLeft, + style.paddingTop, + style.paddingRight, + style.paddingBottom + ) + + if (style.opacity != 0.0f) { + subtitleView.alpha = style.opacity + subtitleView.visibility = android.view.View.VISIBLE + } else { + subtitleView.visibility = android.view.View.GONE } - requestLayout() } localStyle = style } fun setShutterColor(color: Int) { - shutterView.setBackgroundColor(color) + playerView.setShutterBackgroundColor(color) } - fun updateSurfaceView(@ViewType.ViewType viewType: Int) { - this.viewType = viewType - var viewNeedRefresh = false - when (viewType) { - ViewType.VIEW_TYPE_SURFACE, ViewType.VIEW_TYPE_SURFACE_SECURE -> { - if (surfaceView !is SurfaceView) { - surfaceView = SurfaceView(context) - viewNeedRefresh = true + fun updateSurfaceView(viewType: Int) { + val currentSurfaceView = surfaceView + val newSurfaceView: View = when (viewType) { + ViewType.VIEW_TYPE_TEXTURE -> TextureView(context) + + ViewType.VIEW_TYPE_SURFACE_SECURE -> SurfaceView(context).apply { + // Requires API 17+ + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + (this as SurfaceView).setSecure(true) } - (surfaceView as SurfaceView).setSecure(viewType == ViewType.VIEW_TYPE_SURFACE_SECURE) } - ViewType.VIEW_TYPE_TEXTURE -> { - if (surfaceView !is TextureView) { - surfaceView = TextureView(context) - viewNeedRefresh = true - } - // Support opacity properly: - (surfaceView as TextureView).isOpaque = false - } - - else -> { - DebugLog.wtf(TAG, "Unexpected texture view type: $viewType") - } + else -> SurfaceView(context) } - if (viewNeedRefresh) { - surfaceView?.layoutParams = layoutParams - - if (layout.getChildAt(0) != null) { - layout.removeViewAt(0) - } - layout.addView(surfaceView, 0, layoutParams) - - if (this.player != null) { - setVideoView() - } - } - } - - var adsShown = false - fun showAds() { - if (!adsShown) { - adOverlayFrameLayout = FrameLayout(context) - layout.addView(adOverlayFrameLayout, layoutParams) - adsShown = true - } - } - - fun hideAds() { - if (adsShown) { - layout.removeView(adOverlayFrameLayout) - adOverlayFrameLayout = null - adsShown = false - } - } - - fun updateShutterViewVisibility() { - shutterView.visibility = if (this.hideShutterView) { - View.INVISIBLE - } else { - View.VISIBLE - } - } - - override fun requestLayout() { - super.requestLayout() - post(measureAndLayout) - } - - // AdsLoader.AdViewProvider implementation. - override fun getAdViewGroup(): ViewGroup = - Assertions.checkNotNull( - adOverlayFrameLayout, - "exo_ad_overlay must be present for ad playback" - ) - - /** - * Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the - * player will be called and previous - * assignments are overridden. - * - * @param player The {@link ExoPlayer} to use. - */ - fun setPlayer(player: ExoPlayer?) { - if (this.player == player) { + if (currentSurfaceView === newSurfaceView) { return } - if (this.player != null) { - this.player!!.removeListener(componentListener) - clearVideoView() - } - this.player = player - updateShutterViewVisibility() - if (player != null) { - setVideoView() - player.addListener(componentListener) + // Remove the old surface view + if (currentSurfaceView != null) { + removeView(currentSurfaceView) } + + // Add the new surface view + surfaceView = newSurfaceView + val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + addView(newSurfaceView, 0, layoutParams) + + // Attach player to the new surface + playerView.player?.let { + when (newSurfaceView) { + is SurfaceView -> it.setVideoSurfaceView(newSurfaceView) + is TextureView -> it.setVideoTextureView(newSurfaceView) + } + } + } + + val isPlaying: Boolean + get() = playerView.player?.isPlaying ?: false + + fun invalidateAspectRatio() { + // PlayerView handles aspect ratio automatically + requestLayout() + } + + fun setUseController(useController: Boolean) { + playerView.useController = useController + if (useController) { + // Ensure proper touch handling when controls are enabled + playerView.controllerAutoShow = true + playerView.controllerHideOnTouch = true + // Show controls immediately when enabled + playerView.showController() + } + } + + fun showController() { + playerView.showController() + } + + fun hideController() { + playerView.hideController() + } + + fun setControllerShowTimeoutMs(showTimeoutMs: Int) { + playerView.controllerShowTimeoutMs = showTimeoutMs + } + + fun setControllerAutoShow(autoShow: Boolean) { + playerView.controllerAutoShow = autoShow + } + + fun setControllerHideOnTouch(hideOnTouch: Boolean) { + playerView.controllerHideOnTouch = hideOnTouch + } + + fun setFullscreenButtonClickListener(listener: PlayerView.FullscreenButtonClickListener?) { + playerView.setFullscreenButtonClickListener(listener) + } + + fun setShowSubtitleButton(show: Boolean) { + playerView.setShowSubtitleButton(show) + } + + fun isControllerVisible(): Boolean = playerView.isControllerFullyVisible + + fun setControllerVisibilityListener(listener: PlayerView.ControllerVisibilityListener?) { + playerView.setControllerVisibilityListener(listener) + } + + override fun addOnLayoutChangeListener(listener: View.OnLayoutChangeListener) { + playerView.addOnLayoutChangeListener(listener) + } + + override fun setFocusable(focusable: Boolean) { + playerView.isFocusable = focusable + } + + private fun updateLiveUi() { + val player = playerView.player ?: return + val isLive = player.isCurrentMediaItemLive + val seekable = player.isCurrentMediaItemSeekable + + // Show/hide badge + liveBadge.visibility = if (isLive) View.VISIBLE else View.GONE + + // Disable/enable scrubbing based on seekable + val timeBar = playerView.findViewById(androidx.media3.ui.R.id.exo_progress) + timeBar?.isEnabled = !isLive || seekable + } + + private val playerListener = object : Player.Listener { + override fun onTimelineChanged(timeline: Timeline, reason: Int) { + playerView.post { playerView.requestLayout() } + updateLiveUi() + } + + override fun onEvents(player: Player, events: Player.Events) { + if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION) || + events.contains(Player.EVENT_IS_PLAYING_CHANGED) + ) { + updateLiveUi() + } + } + } + + companion object { + private const val TAG = "ExoPlayerView" } /** - * Sets the resize mode which can be of value {@link ResizeMode.Mode} - * - * @param resizeMode The resize mode. + * React Native (Yoga) can sometimes defer layout passes that are required by + * PlayerView for its child views (controller overlay, surface view, subtitle view, …). + * This helper forces a second measure / layout after RN finishes, ensuring the + * internal views receive the final size. The same approach is used in the v7 + * implementation (see VideoView.kt) and in React Native core (Toolbar example [link]). */ - fun setResizeMode(@ResizeMode.Mode resizeMode: Int) { - if (layout.resizeMode != resizeMode) { - layout.resizeMode = resizeMode - post(measureAndLayout) - } - } - - fun setHideShutterView(hideShutterView: Boolean) { - this.hideShutterView = hideShutterView - updateShutterViewVisibility() - } - - private val measureAndLayout: Runnable = Runnable { + private val layoutRunnable = Runnable { measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) @@ -278,60 +277,9 @@ class ExoPlayerView(private val context: Context) : layout(left, top, right, bottom) } - private fun updateForCurrentTrackSelections(tracks: Tracks?) { - if (tracks == null) { - return - } - val groups = tracks.groups - - for (group in groups) { - if (group.type == C.TRACK_TYPE_VIDEO && group.length > 0) { - // get the first track of the group to identify aspect ratio - val format = group.getTrackFormat(0) - if (format.width > 0 || format.height > 0) { - layout.updateAspectRatio(format) - } - return - } - } - // no video tracks, in that case refresh shutterView visibility - updateShutterViewVisibility() - } - - fun invalidateAspectRatio() { - // Resetting aspect ratio will force layout refresh on next video size changed - layout.invalidateAspectRatio() - } - - private inner class ComponentListener : Player.Listener { - override fun onCues(cues: List) { - subtitleLayout.setCues(cues) - } - - override fun onVideoSizeChanged(videoSize: VideoSize) { - if (videoSize.height == 0 || videoSize.width == 0) { - // When changing video track we receive an ghost state with height / width = 0 - // No need to resize the view in that case - return - } - // Here we use updateForCurrentTrackSelections to have a consistent behavior. - // according to: https://github.com/androidx/media/issues/1207 - // sometimes media3 send bad Video size information - player?.let { - updateForCurrentTrackSelections(it.currentTracks) - } - } - - override fun onRenderedFirstFrame() { - shutterView.visibility = INVISIBLE - } - - override fun onTracksChanged(tracks: Tracks) { - updateForCurrentTrackSelections(tracks) - } - } - - companion object { - private const val TAG = "ExoPlayerView" + override fun requestLayout() { + super.requestLayout() + // Post a second layout pass so the ExoPlayer internal views get correct bounds. + post(layoutRunnable) } } diff --git a/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.kt b/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.kt index a0f56675..b5d786b2 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.kt @@ -10,8 +10,6 @@ import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.widget.FrameLayout -import android.widget.ImageButton -import android.widget.LinearLayout import androidx.activity.OnBackPressedCallback import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat @@ -91,7 +89,6 @@ class FullScreenPlayerView( parent?.removeView(exoPlayerView) containerView.addView(exoPlayerView, generateDefaultLayoutParams()) playerControlView?.let { - updateFullscreenButton(playerControlView, true) parent?.removeView(it) containerView.addView(it, generateDefaultLayoutParams()) } @@ -104,7 +101,6 @@ class FullScreenPlayerView( containerView.removeView(exoPlayerView) parent?.addView(exoPlayerView, generateDefaultLayoutParams()) playerControlView?.let { - updateFullscreenButton(playerControlView, false) containerView.removeView(it) parent?.addView(it, generateDefaultLayoutParams()) } @@ -141,20 +137,6 @@ class FullScreenPlayerView( androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter } - private fun updateFullscreenButton(playerControlView: LegacyPlayerControlView, isFullscreen: Boolean) { - val imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen) - imageButton?.let { - val imgResource = getFullscreenIconResource(isFullscreen) - val desc = if (isFullscreen) { - context.getString(androidx.media3.ui.R.string.exo_controls_fullscreen_exit_description) - } else { - context.getString(androidx.media3.ui.R.string.exo_controls_fullscreen_enter_description) - } - imageButton.setImageResource(imgResource) - imageButton.contentDescription = desc - } - } - override fun onAttachedToWindow() { super.onAttachedToWindow() if (reactExoplayerView.preventsDisplaySleepDuringVideoPlayback) { @@ -226,13 +208,7 @@ class FullScreenPlayerView( WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE ) } - if (controlsConfig.hideNotificationBarOnFullScreenMode) { - val liveContainer = playerControlView?.findViewById(com.brentvatne.react.R.id.exo_live_container) - liveContainer?.let { - val layoutParams = it.layoutParams as LinearLayout.LayoutParams - layoutParams.topMargin = 40 - it.layoutParams = layoutParams - } - } + // Note: Live container adjustment is no longer needed since we're using PlayerView's built-in controls + // PlayerView handles UI adjustments automatically } } diff --git a/android/src/main/java/com/brentvatne/exoplayer/PictureInPictureUtil.kt b/android/src/main/java/com/brentvatne/exoplayer/PictureInPictureUtil.kt index 6c2f2fd6..3e3088a2 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/PictureInPictureUtil.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/PictureInPictureUtil.kt @@ -141,9 +141,10 @@ object PictureInPictureUtil { @RequiresApi(Build.VERSION_CODES.O) private fun calcRectHint(playerView: ExoPlayerView): Rect { val hint = Rect() - playerView.surfaceView?.getGlobalVisibleRect(hint) + // Use the PlayerView itself since surfaceView is private + playerView.getGlobalVisibleRect(hint) val location = IntArray(2) - playerView.surfaceView?.getLocationOnScreen(location) + playerView.getLocationOnScreen(location) val height = hint.bottom - hint.top hint.top = location[1] diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 85f8da1e..bea60c09 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -103,7 +103,6 @@ import androidx.media3.extractor.metadata.emsg.EventMessage; import androidx.media3.extractor.metadata.id3.Id3Frame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.session.MediaSessionService; -import androidx.media3.ui.LegacyPlayerControlView; import com.brentvatne.common.api.AdsProps; import com.brentvatne.common.api.BufferConfig; @@ -178,8 +177,6 @@ public class ReactExoplayerView extends FrameLayout implements protected final VideoEventEmitter eventEmitter; private final ReactExoplayerConfig config; private DefaultBandwidthMeter bandwidthMeter; - private LegacyPlayerControlView playerControlView; - private View playPauseControlContainer; private Player.Listener eventListener; private ExoPlayerView exoPlayerView; @@ -248,7 +245,7 @@ public class ReactExoplayerView extends FrameLayout implements private float mProgressUpdateInterval = 250.0f; protected boolean playInBackground = false; private boolean mReportBandwidth = false; - private boolean controls; + private boolean controls = false; private boolean showNotificationControls = false; // \ End props @@ -278,8 +275,8 @@ public class ReactExoplayerView extends FrameLayout implements private void updateProgress() { if (player != null) { - if (playerControlView != null && isPlayingAd() && controls) { - playerControlView.hide(); + if (exoPlayerView != null && isPlayingAd() && controls) { + exoPlayerView.hideController(); } long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100; long duration = player.getDuration(); @@ -423,103 +420,45 @@ public class ReactExoplayerView extends FrameLayout implements */ private void togglePlayerControlVisibility() { if (player == null) return; - reLayoutControls(); - if (playerControlView.isVisible()) { - playerControlView.hide(); + if (exoPlayerView.isControllerVisible()) { + exoPlayerView.hideController(); } else { - playerControlView.show(); + exoPlayerView.showController(); } } - /** - * Initializing Player control - */ private void initializePlayerControl() { - if (playerControlView == null) { - playerControlView = new LegacyPlayerControlView(getContext()); - playerControlView.addVisibilityListener(new LegacyPlayerControlView.VisibilityListener() { - @Override - public void onVisibilityChange(int visibility) { - eventEmitter.onControlsVisibilityChange.invoke(visibility == View.VISIBLE); - } - }); - } - - // Setting the player for the playerControlView - playerControlView.setPlayer(player); - playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container); - - // Invoking onClick event for exoplayerView - exoPlayerView.setOnClickListener((View v) -> { - if (!isPlayingAd()) { - togglePlayerControlVisibility(); - } + exoPlayerView.setPlayer(player); + + exoPlayerView.setControllerVisibilityListener(visibility -> { + boolean isVisible = visibility == View.VISIBLE; + eventEmitter.onControlsVisibilityChange.invoke(isVisible); }); - //Handling the playButton click event - ImageButton playButton = playerControlView.findViewById(R.id.exo_play); - - playButton.setOnClickListener((View v) -> { - if (player != null && player.getPlaybackState() == Player.STATE_ENDED) { - player.seekTo(0); - } - setPausedModifier(false); + exoPlayerView.setFullscreenButtonClickListener(isFullscreen -> { + setFullscreen(!this.isFullscreen); }); - //Handling the rewind and forward button click events - ImageButton exoRewind = playerControlView.findViewById(R.id.exo_rew); - ImageButton exoForward = playerControlView.findViewById(R.id.exo_ffwd); - exoRewind.setOnClickListener((View v) -> { - seekTo(player.getCurrentPosition() - controlsConfig.getSeekIncrementMS()); - }); - - exoForward.setOnClickListener((View v) -> { - seekTo(player.getCurrentPosition() + controlsConfig.getSeekIncrementMS()); - }); - - //Handling the pauseButton click event - ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause); - pauseButton.setOnClickListener((View v) -> - setPausedModifier(true) - ); - - //Handling the settingButton click event - final ImageButton settingButton = playerControlView.findViewById(R.id.exo_settings); - settingButton.setOnClickListener(v -> openSettings()); - - //Handling the fullScreenButton click event - final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen); - fullScreenButton.setOnClickListener(v -> setFullscreen(!isFullscreen)); - updateFullScreenButtonVisibility(); - refreshControlsStyles(); - - // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player - eventListener = new Player.Listener() { - @Override - public void onPlaybackStateChanged(int playbackState) { - View playButton = playerControlView.findViewById(R.id.exo_play); - View pauseButton = playerControlView.findViewById(R.id.exo_pause); - if (playButton != null && playButton.getVisibility() == GONE) { - playButton.setVisibility(INVISIBLE); - } - if (pauseButton != null && pauseButton.getVisibility() == GONE) { - pauseButton.setVisibility(INVISIBLE); - } - - reLayout(playPauseControlContainer); - //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done - player.removeListener(eventListener); - } - - @Override - public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { - reLayout(playPauseControlContainer); - //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done - player.removeListener(eventListener); - } - }; - player.addListener(eventListener); + updateControllerConfig(); } + + private void updateControllerConfig() { + if (exoPlayerView == null) return; + + exoPlayerView.setControllerShowTimeoutMs(5000); + + exoPlayerView.setControllerAutoShow(true); + exoPlayerView.setControllerHideOnTouch(true); + + updateControllerVisibility(); + } + + private void updateControllerVisibility() { + if (exoPlayerView == null) return; + + exoPlayerView.setUseController(!controlsConfig.getHideFullscreen()); + } + private void openSettings() { AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext); builder.setTitle(R.string.settings); @@ -558,21 +497,8 @@ public class ReactExoplayerView extends FrameLayout implements builder.show(); } - /** - * Adding Player control to the frame layout - */ private void addPlayerControl() { - if (playerControlView == null) return; - LayoutParams layoutParams = new LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - playerControlView.setLayoutParams(layoutParams); - int indexOfPC = indexOfChild(playerControlView); - if (indexOfPC != -1) { - removeViewAt(indexOfPC); - } - addView(playerControlView, 1, layoutParams); - reLayout(playerControlView); + updateControllerConfig(); } /** @@ -589,81 +515,16 @@ public class ReactExoplayerView extends FrameLayout implements } private void refreshControlsStyles() { - if (playerControlView == null || player == null || !controls) return; - updateLiveContent(); - updatePlayPauseButtons(); - updateButtonVisibility(controlsConfig.getHideForward(), R.id.exo_ffwd); - updateButtonVisibility(controlsConfig.getHideRewind(), R.id.exo_rew); - updateButtonVisibility(controlsConfig.getHideNext(), R.id.exo_next); - updateButtonVisibility(controlsConfig.getHidePrevious(), R.id.exo_prev); - updateViewVisibility(playerControlView.findViewById(R.id.exo_fullscreen), controlsConfig.getHideFullscreen(), GONE); - updateViewVisibility(playerControlView.findViewById(R.id.exo_position), controlsConfig.getHidePosition(), GONE); - updateViewVisibility(playerControlView.findViewById(R.id.exo_progress), controlsConfig.getHideSeekBar(), INVISIBLE); - updateViewVisibility(playerControlView.findViewById(R.id.exo_duration), controlsConfig.getHideDuration(), GONE); - updateViewVisibility(playerControlView.findViewById(R.id.exo_settings), controlsConfig.getHideSettingButton(), GONE ); + if (exoPlayerView == null || player == null || !controls) return; + updateControllerVisibility(); } - private void updateLiveContent() { - LinearLayout exoLiveContainer = playerControlView.findViewById(R.id.exo_live_container); - TextView exoLiveLabel = playerControlView.findViewById(R.id.exo_live_label); - - boolean isLive = false; - Timeline timeline = player.getCurrentTimeline(); - - // Determine if the content is live - if (!timeline.isEmpty()) { - Timeline.Window window = new Timeline.Window(); - timeline.getWindow(player.getCurrentMediaItemIndex(), window); - isLive = window.isLive(); - } - - if (isLive && controlsConfig.getLiveLabel() != null) { - exoLiveLabel.setText(controlsConfig.getLiveLabel()); - exoLiveContainer.setVisibility(VISIBLE); - } else { - exoLiveContainer.setVisibility(GONE); - } - } - - private void updatePlayPauseButtons() { - final ImageButton playButton = playerControlView.findViewById(R.id.exo_play); - final ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause); - - if (controlsConfig.getHidePlayPause()) { - playPauseControlContainer.setAlpha(0); - playButton.setClickable(false); - pauseButton.setClickable(false); - } else { - playPauseControlContainer.setAlpha(1.0f); - playButton.setClickable(true); - pauseButton.setClickable(true); - } - } - - private void updateButtonVisibility(boolean hide, int buttonID) { - ImageButton button = playerControlView.findViewById(buttonID); - if (hide) { - button.setImageAlpha(0); - button.setClickable(false); - } else { - button.setImageAlpha(255); - button.setClickable(true); - } - } - - private void updateViewVisibility(View view, boolean hide, int hideVisibility) { - if (hide) { - view.setVisibility(hideVisibility); - } else if (view.getVisibility() == hideVisibility) { - view.setVisibility(VISIBLE); - } - } - - + // Note: The following methods for live content and button visibility are no longer needed + // as PlayerView handles controls automatically. Some functionality may need to be + // reimplemented using PlayerView's APIs if custom behavior is required. private void reLayoutControls() { reLayout(exoPlayerView); - reLayout(playerControlView); } /// returns true is adaptive bitrate shall be used @@ -791,7 +652,6 @@ public class ReactExoplayerView extends FrameLayout implements } if (playerNeedsSource) { // Will force display of shutter view if needed - exoPlayerView.updateShutterViewVisibility(); exoPlayerView.invalidateAspectRatio(); // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread ExecutorService es = Executors.newSingleThreadExecutor(); @@ -875,7 +735,7 @@ public class ReactExoplayerView extends FrameLayout implements mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true))); } - mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView); + mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView.getPlayerView()); player = new ExoPlayer.Builder(getContext(), renderersFactory) .setTrackSelector(self.trackSelector) @@ -910,7 +770,6 @@ public class ReactExoplayerView extends FrameLayout implements if (adProps != null && uri != null) { Uri adTagUrl = adProps.getAdTagUrl(); if (adTagUrl != null) { - exoPlayerView.showAds(); // Create an AdsLoader. ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader .Builder(themedReactContext) @@ -926,16 +785,16 @@ public class ReactExoplayerView extends FrameLayout implements adsLoader.setPlayer(player); if (adsLoader != null) { DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory) - .setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView); + .setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView.getPlayerView()); DataSpec adTagDataSpec = new DataSpec(adTagUrl); return new AdsMediaSource(videoSource, adTagDataSpec, ImmutableList.of(uri, adTagUrl), - mediaSourceFactory, adsLoader, exoPlayerView); + mediaSourceFactory, adsLoader, exoPlayerView.getPlayerView()); } } } - exoPlayerView.hideAds(); + return null; } @@ -981,7 +840,7 @@ public class ReactExoplayerView extends FrameLayout implements DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!"); return; } - // init source to manage ads and external text tracks + // init source to manage ads (external text tracks are now handled in MediaItem) MediaSource videoSource = buildMediaSource(runningSource.getUri(), runningSource.getExtension(), drmSessionManager, @@ -990,12 +849,6 @@ public class ReactExoplayerView extends FrameLayout implements MediaSource mediaSourceWithAds = initializeAds(videoSource, runningSource); MediaSource mediaSource = Objects.requireNonNullElse(mediaSourceWithAds, videoSource); - MediaSource subtitlesSource = buildTextSource(); - if (subtitlesSource != null) { - MediaSource[] mediaSourceArray = {mediaSource, subtitlesSource}; - mediaSource = new MergingMediaSource(mediaSourceArray); - } - // wait for player to be set while (player == null) { try { @@ -1151,6 +1004,13 @@ public class ReactExoplayerView extends FrameLayout implements if (customMetadata != null) { mediaItemBuilder.setMediaMetadata(customMetadata); } + + // Add external subtitles to MediaItem + List subtitleConfigurations = buildSubtitleConfigurations(); + if (subtitleConfigurations != null) { + mediaItemBuilder.setSubtitleConfigurations(subtitleConfigurations); + } + if (source.getAdsProps() != null) { Uri adTagUrl = source.getAdsProps().getAdTagUrl(); if (adTagUrl != null) { @@ -1285,29 +1145,59 @@ public class ReactExoplayerView extends FrameLayout implements } @Nullable - private MediaSource buildTextSource() { - if (source.getSideLoadedTextTracks() == null) { + private List buildSubtitleConfigurations() { + if (source.getSideLoadedTextTracks() == null || source.getSideLoadedTextTracks().getTracks().isEmpty()) { return null; } List subtitleConfigurations = new ArrayList<>(); + int trackIndex = 0; for (SideLoadedTextTrack track : source.getSideLoadedTextTracks().getTracks()) { - MediaItem.SubtitleConfiguration subtitleConfiguration = new MediaItem.SubtitleConfiguration.Builder(track.getUri()) - .setMimeType(track.getType()) - .setLanguage(track.getLanguage()) - .setSelectionFlags(C.SELECTION_FLAG_DEFAULT) - .setRoleFlags(C.ROLE_FLAG_SUBTITLE) - .setLabel(track.getTitle()) - .build(); - subtitleConfigurations.add(subtitleConfiguration); + try { + // Create a more descriptive ID that PlayerView can use + String trackId = "external-subtitle-" + trackIndex; + String label = track.getTitle(); + if (label == null || label.isEmpty()) { + label = "External " + (trackIndex + 1); + if (track.getLanguage() != null && !track.getLanguage().isEmpty()) { + label += " (" + track.getLanguage() + ")"; + } + } + + MediaItem.SubtitleConfiguration.Builder configBuilder = new MediaItem.SubtitleConfiguration.Builder(track.getUri()) + .setId(trackId) + .setMimeType(track.getType()) + .setLabel(label) + .setRoleFlags(C.ROLE_FLAG_SUBTITLE); + + // Set language if available + if (track.getLanguage() != null && !track.getLanguage().isEmpty()) { + configBuilder.setLanguage(track.getLanguage()); + } + + // Set selection flags - make first track default if no specific track is selected + if (trackIndex == 0 && (textTrackType == null || "disabled".equals(textTrackType))) { + configBuilder.setSelectionFlags(C.SELECTION_FLAG_DEFAULT); + } else { + configBuilder.setSelectionFlags(0); + } + + MediaItem.SubtitleConfiguration subtitleConfiguration = configBuilder.build(); + subtitleConfigurations.add(subtitleConfiguration); + + DebugLog.d(TAG, "Created subtitle configuration: " + trackId + " - " + label + " (" + track.getType() + ")"); + trackIndex++; + } catch (Exception e) { + DebugLog.e(TAG, "Error creating SubtitleConfiguration for URI " + track.getUri() + ": " + e.getMessage()); + } } - MediaItem subtitlesMediaItem = new MediaItem.Builder() - .setUri(source.getUri()) - .setSubtitleConfigurations(subtitleConfigurations).build(); + if (!subtitleConfigurations.isEmpty()) { + DebugLog.d(TAG, "Built " + subtitleConfigurations.size() + " external subtitle configurations"); + } - return new DefaultMediaSourceFactory(mediaDataSourceFactory).createMediaSource(subtitlesMediaItem); + return subtitleConfigurations.isEmpty() ? null : subtitleConfigurations; } private void releasePlayer() { @@ -1530,9 +1420,9 @@ public class ReactExoplayerView extends FrameLayout implements selectTrackWhenReady = false; setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); } - // Setting the visibility for the playerControlView - if (playerControlView != null) { - playerControlView.show(); + // Setting the visibility for the player controls + if (exoPlayerView != null) { + exoPlayerView.showController(); } setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); break; @@ -1598,7 +1488,8 @@ public class ReactExoplayerView extends FrameLayout implements } eventEmitter.onVideoLoad.invoke(duration, currentPosition, width, height, audioTracks, textTracks, videoTracks, trackId ); - + + updateSubtitleButtonVisibility(); }); return; } @@ -1607,6 +1498,8 @@ public class ReactExoplayerView extends FrameLayout implements eventEmitter.onVideoLoad.invoke(duration, currentPosition, width, height, audioTracks, textTracks, videoTracks, trackId); + + updateSubtitleButtonVisibility(); refreshControlsStyles(); } } @@ -1620,7 +1513,6 @@ public class ReactExoplayerView extends FrameLayout implements private ArrayList getAudioTrackInfo() { ArrayList audioTracks = new ArrayList<>(); if (trackSelector == null) { - // Likely player is unmounting so no audio tracks are available anymore return audioTracks; } @@ -1631,15 +1523,25 @@ public class ReactExoplayerView extends FrameLayout implements } TrackGroupArray groups = info.getTrackGroups(index); TrackSelectionArray selectionArray = player.getCurrentTrackSelections(); - TrackSelection selection = selectionArray.get( C.TRACK_TYPE_AUDIO ); + TrackSelection selection = selectionArray.get(C.TRACK_TYPE_AUDIO); - for (int i = 0; i < groups.length; ++i) { - TrackGroup group = groups.get(i); + + for (int groupIndex = 0; groupIndex < groups.length; ++groupIndex) { + TrackGroup group = groups.get(groupIndex); Format format = group.getFormat(0); - Track audioTrack = exoplayerTrackToGenericTrack(format, i, selection, group); + + // Check if this specific group is the currently selected one + boolean isSelected = false; + if (selection != null && selection.getTrackGroup() == group) { + isSelected = true; + } + + Track audioTrack = exoplayerTrackToGenericTrack(format, groupIndex, selection, group); audioTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); + audioTrack.setSelected(isSelected); audioTracks.add(audioTrack); } + return audioTracks; } @@ -1765,20 +1667,113 @@ public class ReactExoplayerView extends FrameLayout implements if (trackSelector == null) { return textTracks; } + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT); if (info == null || index == C.INDEX_UNSET) { return textTracks; } + TrackSelectionArray selectionArray = player.getCurrentTrackSelections(); - TrackSelection selection = selectionArray.get( C.TRACK_TYPE_VIDEO ); + TrackSelection selection = selectionArray.get(C.TRACK_TYPE_TEXT); TrackGroupArray groups = info.getTrackGroups(index); - for (int i = 0; i < groups.length; ++i) { - TrackGroup group = groups.get(i); + for (int groupIndex = 0; groupIndex < groups.length; ++groupIndex) { + TrackGroup group = groups.get(groupIndex); + for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { + Format format = group.getFormat(trackIndex); + Track textTrack = exoplayerTrackToGenericTrack(format, trackIndex, selection, group); + + boolean isExternal = format.id != null && format.id.startsWith("external-subtitle-"); + boolean isSelected = isTrackSelected(selection, group, trackIndex); + + textTrack.setIndex(textTracks.size()); + + if (textTrack.getTitle() == null || textTrack.getTitle().isEmpty()) { + if (isExternal) { + textTrack.setTitle("External " + (trackIndex + 1)); + } else { + textTrack.setTitle("Track " + (textTracks.size() + 1)); + } + } + + textTracks.add(textTrack); + } + } + return textTracks; + } + + private ArrayList getBasicAudioTrackInfo() { + ArrayList tracks = new ArrayList<>(); + if (trackSelector == null) { + return tracks; + } + + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO); + if (info == null || index == C.INDEX_UNSET) { + return tracks; + } + + TrackGroupArray groups = info.getTrackGroups(index); + + for (int groupIndex = 0; groupIndex < groups.length; ++groupIndex) { + TrackGroup group = groups.get(groupIndex); Format format = group.getFormat(0); - Track textTrack = exoplayerTrackToGenericTrack(format, i, selection, group); - textTracks.add(textTrack); + + // Create track without trying to determine selection status + Track track = new Track(); + track.setIndex(groupIndex); + track.setLanguage(format.language != null ? format.language : "unknown"); + track.setTitle(format.label != null ? format.label : "Track " + (groupIndex + 1)); + track.setSelected(false); // Don't report selection status - let PlayerView handle it + if (format.sampleMimeType != null) track.setMimeType(format.sampleMimeType); + track.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); + + tracks.add(track); + } + + DebugLog.d(TAG, "getBasicAudioTrackInfo: returning " + tracks.size() + " audio tracks (no selection status)"); + return tracks; + } + + private ArrayList getBasicTextTrackInfo() { + ArrayList textTracks = new ArrayList<>(); + if (trackSelector == null) { + return textTracks; + } + + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT); + if (info == null || index == C.INDEX_UNSET) { + return textTracks; + } + + TrackGroupArray groups = info.getTrackGroups(index); + + for (int groupIndex = 0; groupIndex < groups.length; ++groupIndex) { + TrackGroup group = groups.get(groupIndex); + for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { + Format format = group.getFormat(trackIndex); + + Track textTrack = new Track(); + textTrack.setIndex(textTracks.size()); + if (format.sampleMimeType != null) textTrack.setMimeType(format.sampleMimeType); + if (format.language != null) textTrack.setLanguage(format.language); + + boolean isExternal = format.id != null && format.id.startsWith("external-subtitle-"); + + if (format.label != null && !format.label.isEmpty()) { + textTrack.setTitle(format.label); + } else if (isExternal) { + textTrack.setTitle("External " + (trackIndex + 1)); + } else { + textTrack.setTitle("Track " + (textTracks.size() + 1)); + } + + textTrack.setSelected(false); // Don't report selection status - let PlayerView handle it + textTracks.add(textTrack); + } } return textTracks; } @@ -1835,9 +1830,70 @@ public class ReactExoplayerView extends FrameLayout implements @Override public void onTracksChanged(@NonNull Tracks tracks) { - eventEmitter.onTextTracks.invoke(getTextTrackInfo()); - eventEmitter.onAudioTracks.invoke(getAudioTrackInfo()); - eventEmitter.onVideoTracks.invoke(getVideoTrackInfo()); + DebugLog.d(TAG, "onTracksChanged called - updating track information, controls=" + controls); + + if (controls) { + ArrayList textTracks = getBasicTextTrackInfo(); + ArrayList audioTracks = getBasicAudioTrackInfo(); + ArrayList videoTracks = getVideoTrackInfo(); + + eventEmitter.onTextTracks.invoke(textTracks); + eventEmitter.onAudioTracks.invoke(audioTracks); + eventEmitter.onVideoTracks.invoke(videoTracks); + } else { + ArrayList textTracks = getTextTrackInfo(); + ArrayList audioTracks = getAudioTrackInfo(); + ArrayList videoTracks = getVideoTrackInfo(); + + eventEmitter.onTextTracks.invoke(textTracks); + eventEmitter.onAudioTracks.invoke(audioTracks); + eventEmitter.onVideoTracks.invoke(videoTracks); + int selectedAudioTracks = 0; + for (Track track : audioTracks) { + if (track.isSelected()) { + selectedAudioTracks++; + } + } + } + + updateSubtitleButtonVisibility(); + } + + + private boolean hasBuiltInTextTracks() { + if (player == null || trackSelector == null) return false; + + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + if (info == null) return false; + + int textRendererIndex = getTrackRendererIndex(C.TRACK_TYPE_TEXT); + if (textRendererIndex == C.INDEX_UNSET) return false; + + TrackGroupArray groups = info.getTrackGroups(textRendererIndex); + + // Check if any groups have tracks that are NOT external subtitles + for (int i = 0; i < groups.length; i++) { + TrackGroup group = groups.get(i); + for (int j = 0; j < group.length; j++) { + Format format = group.getFormat(j); + // If track ID doesn't start with "external-subtitle-", it's built-in + if (format.id == null || !format.id.startsWith("external-subtitle-")) { + return true; + } + } + } + + return false; + } + + private void updateSubtitleButtonVisibility() { + if (exoPlayerView == null) return; + + boolean hasTextTracks = (source.getSideLoadedTextTracks() != null && + !source.getSideLoadedTextTracks().getTracks().isEmpty()) || + hasBuiltInTextTracks(); + + exoPlayerView.setShowSubtitleButton(hasTextTracks); } @Override @@ -1988,7 +2044,7 @@ public class ReactExoplayerView extends FrameLayout implements player.clearMediaItems(); } } - exoPlayerView.hideAds(); + this.source = new Source(); this.mediaDataSourceFactory = null; clearResumePosition(); @@ -2029,6 +2085,8 @@ public class ReactExoplayerView extends FrameLayout implements } public void disableTrack(int rendererIndex) { + if (trackSelector == null) return; + DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters() .buildUpon() .setRendererDisabled(rendererIndex, true) @@ -2036,12 +2094,89 @@ public class ReactExoplayerView extends FrameLayout implements trackSelector.setParameters(disableParameters); } + private void selectTextTrackInternal(String type, String value) { + if (player == null || trackSelector == null) return; + + DebugLog.d(TAG, "selectTextTrackInternal: type=" + type + ", value=" + value); + + DefaultTrackSelector.Parameters.Builder parametersBuilder = trackSelector.getParameters().buildUpon(); + + if ("disabled".equals(type) || value == null) { + parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true); + } else { + parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, false); + + parametersBuilder.clearOverridesOfType(C.TRACK_TYPE_TEXT); + + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + if (info != null) { + int textRendererIndex = getTrackRendererIndex(C.TRACK_TYPE_TEXT); + if (textRendererIndex != C.INDEX_UNSET) { + TrackGroupArray groups = info.getTrackGroups(textRendererIndex); + boolean trackFound = false; + + for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { + TrackGroup group = groups.get(groupIndex); + for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { + Format format = group.getFormat(trackIndex); + + boolean isMatch = false; + if ("language".equals(type) && format.language != null && format.language.equals(value)) { + isMatch = true; + } else if ("title".equals(type) && format.label != null && format.label.equals(value)) { + isMatch = true; + } else if ("index".equals(type)) { + int targetIndex = ReactBridgeUtils.safeParseInt(value, -1); + if (targetIndex == trackIndex) { + isMatch = true; + } + } + + if (isMatch) { + TrackSelectionOverride override = new TrackSelectionOverride(group, + java.util.Arrays.asList(trackIndex)); + parametersBuilder.addOverride(override); + trackFound = true; + break; + } + } + if (trackFound) break; + } + + if (!trackFound) { + DebugLog.w(TAG, "Text track not found for type=" + type + ", value=" + value + + ". Keeping current selection."); + } + } + } + } + + try { + trackSelector.setParameters(parametersBuilder.build()); + + // Give PlayerView time to update its controls + mainHandler.postDelayed(() -> { + if (exoPlayerView != null) { + updateSubtitleButtonVisibility(); + } + }, 100); + } catch (Exception e) { + DebugLog.e(TAG, "Error setting text track parameters: " + e.getMessage()); + } + } + public void setSelectedTrack(int trackType, String type, String value) { - if (player == null) return; + if (player == null || trackSelector == null) return; + + if (controls) { + return; + } + int rendererIndex = getTrackRendererIndex(trackType); if (rendererIndex == C.INDEX_UNSET) { return; } + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); if (info == null) { return; @@ -2146,7 +2281,7 @@ public class ReactExoplayerView extends FrameLayout implements if (captioningManager != null && captioningManager.isEnabled()) { groupIndex = getGroupIndexForDefaultLocale(groups); } - } else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default + } else if (trackType == C.TRACK_TYPE_AUDIO) { // Audio default groupIndex = getGroupIndexForDefaultLocale(groups); } @@ -2186,23 +2321,42 @@ public class ReactExoplayerView extends FrameLayout implements return; } - TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks); + try { + TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks); - DefaultTrackSelector.Parameters.Builder selectionParameters = trackSelector.getParameters() - .buildUpon() - .setExceedAudioConstraintsIfNecessary(true) - .setExceedRendererCapabilitiesIfNecessary(true) - .setExceedVideoConstraintsIfNecessary(true) - .setRendererDisabled(rendererIndex, false) - .clearOverridesOfType(selectionOverride.getType()); + DefaultTrackSelector.Parameters.Builder selectionParameters = trackSelector.getParameters() + .buildUpon() + .setExceedAudioConstraintsIfNecessary(true) + .setExceedRendererCapabilitiesIfNecessary(true) + .setExceedVideoConstraintsIfNecessary(true) + .setRendererDisabled(rendererIndex, false); - if (trackType == C.TRACK_TYPE_VIDEO && isUsingVideoABR()) { - selectionParameters.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate); - } else { - selectionParameters.addOverride(selectionOverride); + // Clear existing overrides for this track type to avoid conflicts + // But be careful with audio tracks - don't clear unless explicitly selecting a different track + if (trackType != C.TRACK_TYPE_AUDIO || !type.equals("default")) { + selectionParameters.clearOverridesOfType(selectionOverride.getType()); + } + + if (trackType == C.TRACK_TYPE_VIDEO && isUsingVideoABR()) { + selectionParameters.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate); + } else { + selectionParameters.addOverride(selectionOverride); + } + + // For audio tracks, ensure audio renderer stays enabled + if (trackType == C.TRACK_TYPE_AUDIO) { + selectionParameters.setForceHighestSupportedBitrate(false); + selectionParameters.setForceLowestBitrate(false); + DebugLog.d(TAG, "Audio track selection: group=" + groupIndex + ", tracks=" + tracks + + ", override=" + selectionOverride); + } + + trackSelector.setParameters(selectionParameters.build()); + DebugLog.d(TAG, "Applied track selection for type: " + trackType + ", group: " + groupIndex); + } catch (Exception e) { + DebugLog.e(TAG, "Error applying track selection: " + e.getMessage()); + e.printStackTrace(); } - - trackSelector.setParameters(selectionParameters.build()); } private boolean isFormatSupported(Format format) { @@ -2252,13 +2406,17 @@ public class ReactExoplayerView extends FrameLayout implements public void setSelectedAudioTrack(String type, String value) { audioTrackType = type; audioTrackValue = value; - setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue); + + if (!controls && player != null && trackSelector != null) { + setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue); + } } public void setSelectedTextTrack(String type, String value) { textTrackType = type; textTrackValue = value; - setSelectedTrack(C.TRACK_TYPE_TEXT, textTrackType, textTrackValue); + + selectTextTrackInternal(type, value); } public void setPausedModifier(boolean paused) { @@ -2452,18 +2610,6 @@ public class ReactExoplayerView extends FrameLayout implements return preventsDisplaySleepDuringVideoPlayback; } - private void updateFullScreenButtonVisibility() { - if (playerControlView != null) { - final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen); - //Handling the fullScreenButton click event - if (isFullscreen && fullScreenPlayerView != null && !fullScreenPlayerView.isShowing()) { - fullScreenButton.setVisibility(GONE); - } else { - fullScreenButton.setVisibility(VISIBLE); - } - } - } - public void setDisableDisconnectError(boolean disableDisconnectError) { this.disableDisconnectError = disableDisconnectError; } @@ -2480,7 +2626,7 @@ public class ReactExoplayerView extends FrameLayout implements } if (isFullscreen) { - fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, this, playerControlView, new OnBackPressedCallback(true) { + fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, this, null, new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { setFullscreen(false); @@ -2504,12 +2650,6 @@ public class ReactExoplayerView extends FrameLayout implements eventEmitter.onVideoFullscreenPlayerDidDismiss.invoke(); }); } - // need to be done at the end to avoid hiding fullscreen control button when fullScreenPlayerView is shown - updateFullScreenButtonVisibility(); - } - - public void setHideShutterView(boolean hideShutterView) { - exoPlayerView.setHideShutterView(hideShutterView); } @Override @@ -2550,14 +2690,17 @@ public class ReactExoplayerView extends FrameLayout implements */ public void setControls(boolean controls) { this.controls = controls; + if (exoPlayerView != null) { + exoPlayerView.setUseController(controls); + // Additional configuration for proper touch handling + if (controls) { + exoPlayerView.setControllerAutoShow(true); + exoPlayerView.setControllerHideOnTouch(true); // Show controls on touch, don't hide + exoPlayerView.setControllerShowTimeoutMs(5000); + } + } if (controls) { addPlayerControl(); - updateFullScreenButtonVisibility(); - } else { - int indexOfPC = indexOfChild(playerControlView); - if (indexOfPC != -1) { - removeViewAt(indexOfPC); - } } refreshControlsStyles(); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt index d05bb48e..b9e06564 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt @@ -52,7 +52,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View private const val PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack" private const val PROP_SELECTED_VIDEO_TRACK_TYPE = "type" private const val PROP_SELECTED_VIDEO_TRACK_VALUE = "value" - private const val PROP_HIDE_SHUTTER_VIEW = "hideShutterView" private const val PROP_CONTROLS = "controls" private const val PROP_SUBTITLE_STYLE = "subtitleStyle" private const val PROP_SHUTTER_COLOR = "shutterColor" @@ -227,11 +226,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View videoView.setViewType(viewType) } - @ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false) - fun setHideShutterView(videoView: ReactExoplayerView, hideShutterView: Boolean) { - videoView.setHideShutterView(hideShutterView) - } - @ReactProp(name = PROP_CONTROLS, defaultBoolean = false) fun setControls(videoView: ReactExoplayerView, controls: Boolean) { videoView.setControls(controls) diff --git a/examples/common/BasicExample.tsx b/examples/common/BasicExample.tsx index ba245b06..cd8212c6 100644 --- a/examples/common/BasicExample.tsx +++ b/examples/common/BasicExample.tsx @@ -75,6 +75,10 @@ const BasicExample = () => { useState(false); const [isSeeking, setIsSeeking] = useState(false); + // Add refs to store previous track data for comparison + const previousAudioTracksRef = useRef([]); + const previousTextTracksRef = useRef([]); + const videoRef = useRef(null); const viewStyle = fullscreen ? styles.fullScreen : styles.halfScreen; const currentSrc = srcList[srcListId]; @@ -107,7 +111,36 @@ const BasicExample = () => { const onAudioTracks = (data: OnAudioTracksData) => { console.log('onAudioTracks', data); - const selectedTrack = data.audioTracks?.find((x: AudioTrack) => { + + // Check if audio tracks have actually changed + const currentTracks = data.audioTracks || []; + const previousTracks = previousAudioTracksRef.current; + + // Simple comparison - check if tracks array length or selected track changed + const tracksChanged = + currentTracks.length !== previousTracks.length || + JSON.stringify( + currentTracks.map((t) => ({ + index: t.index, + selected: t.selected, + language: t.language, + })), + ) !== + JSON.stringify( + previousTracks.map((t) => ({ + index: t.index, + selected: t.selected, + language: t.language, + })), + ); + + if (!tracksChanged) { + return; // Skip if tracks haven't changed + } + + previousAudioTracksRef.current = currentTracks; + + const selectedTrack = currentTracks.find((x: AudioTrack) => { return x.selected; }); let value; @@ -118,7 +151,7 @@ const BasicExample = () => { } else if (audioTracksSelectionBy === SelectedTrackType.TITLE) { value = selectedTrack?.title; } - setAudioTracks(data.audioTracks); + setAudioTracks(currentTracks); setSelectedAudioTrack({ type: audioTracksSelectionBy, value: value, @@ -131,11 +164,39 @@ const BasicExample = () => { }; const onTextTracks = (data: OnTextTracksData) => { - const selectedTrack = data.textTracks?.find((x: TextTrack) => { + // Check if text tracks have actually changed + const currentTracks = data.textTracks || []; + const previousTracks = previousTextTracksRef.current; + + // Simple comparison - check if tracks array length or selected track changed + const tracksChanged = + currentTracks.length !== previousTracks.length || + JSON.stringify( + currentTracks.map((t) => ({ + index: t.index, + selected: t.selected, + language: t.language, + })), + ) !== + JSON.stringify( + previousTracks.map((t) => ({ + index: t.index, + selected: t.selected, + language: t.language, + })), + ); + + if (!tracksChanged) { + return; // Skip if tracks haven't changed + } + + previousTextTracksRef.current = currentTracks; + + const selectedTrack = currentTracks.find((x: TextTrack) => { return x?.selected; }); - setTextTracks(data.textTracks); + setTextTracks(currentTracks); let value; if (textTracksSelectionBy === SelectedTrackType.INDEX) { value = selectedTrack?.index; diff --git a/examples/common/components/AudioTracksSelector.tsx b/examples/common/components/AudioTracksSelector.tsx index d0e10059..7d30152f 100644 --- a/examples/common/components/AudioTracksSelector.tsx +++ b/examples/common/components/AudioTracksSelector.tsx @@ -28,7 +28,7 @@ export const AudioTrackSelector = ({ style={styles.picker} itemStyle={styles.pickerItem} selectedValue={selectedAudioTrack?.value} - onValueChange={itemValue => { + onValueChange={(itemValue) => { if (itemValue !== 'empty') { console.log('on audio value change ' + itemValue); onValueChange(itemValue); @@ -39,7 +39,7 @@ export const AudioTrackSelector = ({ ) : ( )} - {audioTracks.map(track => { + {audioTracks.map((track) => { if (!track) { return; } @@ -53,7 +53,9 @@ export const AudioTrackSelector = ({ } return ( diff --git a/examples/common/components/TextTracksSelector.tsx b/examples/common/components/TextTracksSelector.tsx index b1fb04ff..24f9bbe2 100644 --- a/examples/common/components/TextTracksSelector.tsx +++ b/examples/common/components/TextTracksSelector.tsx @@ -28,7 +28,7 @@ export const TextTrackSelector = ({ style={styles.picker} itemStyle={styles.pickerItem} selectedValue={`${selectedTextTrack?.value}`} - onValueChange={itemValue => { + onValueChange={(itemValue) => { if (itemValue !== 'empty') { onValueChange(itemValue); } @@ -38,7 +38,7 @@ export const TextTrackSelector = ({ ) : ( )} - {textTracks.map(track => { + {textTracks.map((track) => { if (!track) { return; } @@ -50,7 +50,15 @@ export const TextTrackSelector = ({ } else if (textTracksSelectionBy === SelectedTrackType.TITLE) { value = track.title; } - return ; + return ( + + ); })} diff --git a/examples/common/constants/general.ts b/examples/common/constants/general.ts index 3db84749..c5f83d67 100644 --- a/examples/common/constants/general.ts +++ b/examples/common/constants/general.ts @@ -120,9 +120,28 @@ export const srcAllPlatformList = [ ad: { adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=', - }, + }, uri: 'https://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4', }, + { + description: 'sintel with subtitles', + uri: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8', + }, + { + description: '(hls) Apple Advanced', + uri: 'https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8', + type: 'm3u8', + }, + { + description: 'elephants dream - multi-language audio & subtitles', + uri: 'https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/index.m3u8', + type: 'm3u8', + }, + { + description: 'tears of steel', + uri: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8', + type: 'm3u8', + }, ]; export const srcIosList: SampleVideoSource[] = []; @@ -172,6 +191,25 @@ export const srcAndroidList: SampleVideoSource[] = [ uri: 'rtsp://rtspstream:3cfa3c36a9c00f4aa38f3cd35816b287@zephyr.rtsp.stream/movie', type: 'rtsp', }, + { + description: 'sintel with subtitles', + uri: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8', + }, + { + description: '(hls) Apple Advanced', + uri: 'https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8', + type: 'm3u8', + }, + { + description: 'elephants dream - multi-language audio & subtitles', + uri: 'https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/index.m3u8', + type: 'm3u8', + }, + { + description: 'tears of steel', + uri: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8', + type: 'm3u8', + }, ]; const platformSrc: SampleVideoSource[] = isAndroid diff --git a/examples/expo/ios/ExpoExample.xcodeproj/project.pbxproj b/examples/expo/ios/ExpoExample.xcodeproj/project.pbxproj index 05569c56..4e3d987a 100644 --- a/examples/expo/ios/ExpoExample.xcodeproj/project.pbxproj +++ b/examples/expo/ios/ExpoExample.xcodeproj/project.pbxproj @@ -357,11 +357,11 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.ExpoExample; - PRODUCT_NAME = "ExpoExample"; + PRODUCT_NAME = ExpoExample; SWIFT_OBJC_BRIDGING_HEADER = "ExpoExample/ExpoExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -385,10 +385,10 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.ExpoExample; - PRODUCT_NAME = "ExpoExample"; + PRODUCT_NAME = ExpoExample; SWIFT_OBJC_BRIDGING_HEADER = "ExpoExample/ExpoExample-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1"; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/examples/expo/ios/Podfile.lock b/examples/expo/ios/Podfile.lock index 29470433..17cb7d92 100644 --- a/examples/expo/ios/Podfile.lock +++ b/examples/expo/ios/Podfile.lock @@ -17,7 +17,7 @@ PODS: - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -32,59 +32,64 @@ PODS: - React-RCTFabric - React-rendererdebug - React-utils + - ReactAppDependencyProvider - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - ExpoSplashScreen (0.29.24): - ExpoModulesCore - - FBLazyVector (0.76.2-0) + - fast_float (6.1.4) + - FBLazyVector (0.77.2-0) - fmt (9.1.0) - glog (0.3.5) - - hermes-engine (0.76.2-0): - - hermes-engine/Pre-built (= 0.76.2-0) - - hermes-engine/Pre-built (0.76.2-0) - - RCT-Folly (2024.01.01.00): + - hermes-engine (0.77.2): + - hermes-engine/Pre-built (= 0.77.2) + - hermes-engine/Pre-built (0.77.2) + - RCT-Folly (2024.11.18.00): - boost - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - - RCT-Folly/Default (= 2024.01.01.00) - - RCT-Folly/Default (2024.01.01.00): + - RCT-Folly/Default (= 2024.11.18.00) + - RCT-Folly/Default (2024.11.18.00): - boost - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - - RCT-Folly/Fabric (2024.01.01.00): + - RCT-Folly/Fabric (2024.11.18.00): - boost - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - - RCTDeprecation (0.76.2-0) - - RCTRequired (0.76.2-0) - - RCTTypeSafety (0.76.2-0): - - FBLazyVector (= 0.76.2-0) - - RCTRequired (= 0.76.2-0) - - React-Core (= 0.76.2-0) - - React (0.76.2-0): - - React-Core (= 0.76.2-0) - - React-Core/DevSupport (= 0.76.2-0) - - React-Core/RCTWebSocket (= 0.76.2-0) - - React-RCTActionSheet (= 0.76.2-0) - - React-RCTAnimation (= 0.76.2-0) - - React-RCTBlob (= 0.76.2-0) - - React-RCTImage (= 0.76.2-0) - - React-RCTLinking (= 0.76.2-0) - - React-RCTNetwork (= 0.76.2-0) - - React-RCTSettings (= 0.76.2-0) - - React-RCTText (= 0.76.2-0) - - React-callinvoker (0.76.2-0) - - React-Core (0.76.2-0): + - RCTDeprecation (0.77.2-0) + - RCTRequired (0.77.2-0) + - RCTTypeSafety (0.77.2-0): + - FBLazyVector (= 0.77.2-0) + - RCTRequired (= 0.77.2-0) + - React-Core (= 0.77.2-0) + - React (0.77.2-0): + - React-Core (= 0.77.2-0) + - React-Core/DevSupport (= 0.77.2-0) + - React-Core/RCTWebSocket (= 0.77.2-0) + - React-RCTActionSheet (= 0.77.2-0) + - React-RCTAnimation (= 0.77.2-0) + - React-RCTBlob (= 0.77.2-0) + - React-RCTImage (= 0.77.2-0) + - React-RCTLinking (= 0.77.2-0) + - React-RCTNetwork (= 0.77.2-0) + - React-RCTSettings (= 0.77.2-0) + - React-RCTText (= 0.77.2-0) + - React-callinvoker (0.77.2-0) + - React-Core (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.76.2-0) + - React-Core/Default (= 0.77.2-0) - React-cxxreact - React-featureflags - React-hermes @@ -96,10 +101,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/CoreModulesHeaders (0.76.2-0): + - React-Core/CoreModulesHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -113,10 +118,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/Default (0.76.2-0): + - React-Core/Default (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-cxxreact - React-featureflags @@ -129,13 +134,13 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/DevSupport (0.76.2-0): + - React-Core/DevSupport (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.76.2-0) - - React-Core/RCTWebSocket (= 0.76.2-0) + - React-Core/Default (= 0.77.2-0) + - React-Core/RCTWebSocket (= 0.77.2-0) - React-cxxreact - React-featureflags - React-hermes @@ -147,10 +152,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.76.2-0): + - React-Core/RCTActionSheetHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -164,10 +169,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTAnimationHeaders (0.76.2-0): + - React-Core/RCTAnimationHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -181,10 +186,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTBlobHeaders (0.76.2-0): + - React-Core/RCTBlobHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -198,10 +203,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTImageHeaders (0.76.2-0): + - React-Core/RCTImageHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -215,10 +220,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTLinkingHeaders (0.76.2-0): + - React-Core/RCTLinkingHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -232,10 +237,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTNetworkHeaders (0.76.2-0): + - React-Core/RCTNetworkHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -249,10 +254,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTSettingsHeaders (0.76.2-0): + - React-Core/RCTSettingsHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -266,10 +271,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTTextHeaders (0.76.2-0): + - React-Core/RCTTextHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -283,10 +288,10 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTVibrationHeaders (0.76.2-0): + - React-Core/RCTVibrationHeaders (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - React-Core/Default - React-cxxreact @@ -300,12 +305,12 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-Core/RCTWebSocket (0.76.2-0): + - React-Core/RCTWebSocket (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTDeprecation - - React-Core/Default (= 0.76.2-0) + - React-Core/Default (= 0.77.2-0) - React-cxxreact - React-featureflags - React-hermes @@ -317,109 +322,86 @@ PODS: - React-utils - SocketRocket (= 0.7.1) - Yoga - - React-CoreModules (0.76.2-0): + - React-CoreModules (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - - RCT-Folly (= 2024.01.01.00) - - RCTTypeSafety (= 0.76.2-0) - - React-Core/CoreModulesHeaders (= 0.76.2-0) - - React-jsi (= 0.76.2-0) + - RCT-Folly (= 2024.11.18.00) + - RCTTypeSafety (= 0.77.2-0) + - React-Core/CoreModulesHeaders (= 0.77.2-0) + - React-jsi (= 0.77.2-0) - React-jsinspector - React-NativeModulesApple - React-RCTBlob - - React-RCTImage (= 0.76.2-0) - - ReactCodegen + - React-RCTFBReactNativeSpec + - React-RCTImage (= 0.77.2-0) - ReactCommon - SocketRocket (= 0.7.1) - - React-cxxreact (0.76.2-0): + - React-cxxreact (0.77.2-0): - boost - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.76.2-0) - - React-debug (= 0.76.2-0) - - React-jsi (= 0.76.2-0) + - RCT-Folly (= 2024.11.18.00) + - React-callinvoker (= 0.77.2-0) + - React-debug (= 0.77.2-0) + - React-jsi (= 0.77.2-0) - React-jsinspector - - React-logger (= 0.76.2-0) - - React-perflogger (= 0.76.2-0) - - React-runtimeexecutor (= 0.76.2-0) - - React-timing (= 0.76.2-0) - - React-debug (0.76.2-0) - - React-defaultsnativemodule (0.76.2-0): - - DoubleConversion - - glog + - React-logger (= 0.77.2-0) + - React-perflogger (= 0.77.2-0) + - React-runtimeexecutor (= 0.77.2-0) + - React-timing (= 0.77.2-0) + - React-debug (0.77.2-0) + - React-defaultsnativemodule (0.77.2-0): - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug + - RCT-Folly - React-domnativemodule - - React-Fabric - - React-featureflags - React-featureflagsnativemodule - - React-graphics - React-idlecallbacksnativemodule - - React-ImageManager + - React-jsi + - React-jsiexecutor - React-microtasksnativemodule - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - React-domnativemodule (0.76.2-0): - - DoubleConversion - - glog + - React-RCTFBReactNativeSpec + - React-domnativemodule (0.77.2-0): - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug + - RCT-Folly - React-Fabric - React-FabricComponents - - React-featureflags - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - Yoga - - React-Fabric (0.76.2-0): + - React-Fabric (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.76.2-0) - - React-Fabric/attributedstring (= 0.76.2-0) - - React-Fabric/componentregistry (= 0.76.2-0) - - React-Fabric/componentregistrynative (= 0.76.2-0) - - React-Fabric/components (= 0.76.2-0) - - React-Fabric/core (= 0.76.2-0) - - React-Fabric/dom (= 0.76.2-0) - - React-Fabric/imagemanager (= 0.76.2-0) - - React-Fabric/leakchecker (= 0.76.2-0) - - React-Fabric/mounting (= 0.76.2-0) - - React-Fabric/observers (= 0.76.2-0) - - React-Fabric/scheduler (= 0.76.2-0) - - React-Fabric/telemetry (= 0.76.2-0) - - React-Fabric/templateprocessor (= 0.76.2-0) - - React-Fabric/uimanager (= 0.76.2-0) + - React-Fabric/animations (= 0.77.2-0) + - React-Fabric/attributedstring (= 0.77.2-0) + - React-Fabric/componentregistry (= 0.77.2-0) + - React-Fabric/componentregistrynative (= 0.77.2-0) + - React-Fabric/components (= 0.77.2-0) + - React-Fabric/core (= 0.77.2-0) + - React-Fabric/dom (= 0.77.2-0) + - React-Fabric/imagemanager (= 0.77.2-0) + - React-Fabric/leakchecker (= 0.77.2-0) + - React-Fabric/mounting (= 0.77.2-0) + - React-Fabric/observers (= 0.77.2-0) + - React-Fabric/scheduler (= 0.77.2-0) + - React-Fabric/telemetry (= 0.77.2-0) + - React-Fabric/templateprocessor (= 0.77.2-0) + - React-Fabric/uimanager (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -429,12 +411,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (0.76.2-0): + - React-Fabric/animations (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -449,12 +432,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.76.2-0): + - React-Fabric/attributedstring (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -469,12 +453,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.76.2-0): + - React-Fabric/componentregistry (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -489,12 +474,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.76.2-0): + - React-Fabric/componentregistrynative (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -509,20 +495,21 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (0.76.2-0): + - React-Fabric/components (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.76.2-0) - - React-Fabric/components/root (= 0.76.2-0) - - React-Fabric/components/view (= 0.76.2-0) + - React-Fabric/components/legacyviewmanagerinterop (= 0.77.2-0) + - React-Fabric/components/root (= 0.77.2-0) + - React-Fabric/components/view (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -532,12 +519,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.76.2-0): + - React-Fabric/components/legacyviewmanagerinterop (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -552,12 +540,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.76.2-0): + - React-Fabric/components/root (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -572,12 +561,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.76.2-0): + - React-Fabric/components/view (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -593,12 +583,13 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (0.76.2-0): + - React-Fabric/core (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -613,12 +604,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/dom (0.76.2-0): + - React-Fabric/dom (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -633,12 +625,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.76.2-0): + - React-Fabric/imagemanager (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -653,12 +646,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.76.2-0): + - React-Fabric/leakchecker (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -673,12 +667,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.76.2-0): + - React-Fabric/mounting (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -693,18 +688,19 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/observers (0.76.2-0): + - React-Fabric/observers (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - - React-Fabric/observers/events (= 0.76.2-0) + - React-Fabric/observers/events (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -714,12 +710,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/observers/events (0.76.2-0): + - React-Fabric/observers/events (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -734,12 +731,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.76.2-0): + - React-Fabric/scheduler (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -756,12 +754,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.76.2-0): + - React-Fabric/telemetry (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -776,12 +775,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.76.2-0): + - React-Fabric/templateprocessor (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -796,18 +796,19 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.76.2-0): + - React-Fabric/uimanager (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - - React-Fabric/uimanager/consistency (= 0.76.2-0) + - React-Fabric/uimanager/consistency (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -818,12 +819,13 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager/consistency (0.76.2-0): + - React-Fabric/uimanager/consistency (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -839,20 +841,21 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricComponents (0.76.2-0): + - React-FabricComponents (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components (= 0.76.2-0) - - React-FabricComponents/textlayoutmanager (= 0.76.2-0) + - React-FabricComponents/components (= 0.77.2-0) + - React-FabricComponents/textlayoutmanager (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -861,30 +864,30 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components (0.76.2-0): + - React-FabricComponents/components (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.76.2-0) - - React-FabricComponents/components/iostextinput (= 0.76.2-0) - - React-FabricComponents/components/modal (= 0.76.2-0) - - React-FabricComponents/components/rncore (= 0.76.2-0) - - React-FabricComponents/components/safeareaview (= 0.76.2-0) - - React-FabricComponents/components/scrollview (= 0.76.2-0) - - React-FabricComponents/components/text (= 0.76.2-0) - - React-FabricComponents/components/textinput (= 0.76.2-0) - - React-FabricComponents/components/unimplementedview (= 0.76.2-0) + - React-FabricComponents/components/inputaccessory (= 0.77.2-0) + - React-FabricComponents/components/iostextinput (= 0.77.2-0) + - React-FabricComponents/components/modal (= 0.77.2-0) + - React-FabricComponents/components/rncore (= 0.77.2-0) + - React-FabricComponents/components/safeareaview (= 0.77.2-0) + - React-FabricComponents/components/scrollview (= 0.77.2-0) + - React-FabricComponents/components/text (= 0.77.2-0) + - React-FabricComponents/components/textinput (= 0.77.2-0) + - React-FabricComponents/components/unimplementedview (= 0.77.2-0) - React-featureflags - React-graphics - React-jsi @@ -893,15 +896,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/inputaccessory (0.76.2-0): + - React-FabricComponents/components/inputaccessory (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -916,15 +919,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/iostextinput (0.76.2-0): + - React-FabricComponents/components/iostextinput (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -939,15 +942,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/modal (0.76.2-0): + - React-FabricComponents/components/modal (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -962,15 +965,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/rncore (0.76.2-0): + - React-FabricComponents/components/rncore (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -985,15 +988,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/safeareaview (0.76.2-0): + - React-FabricComponents/components/safeareaview (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1008,15 +1011,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/scrollview (0.76.2-0): + - React-FabricComponents/components/scrollview (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1031,15 +1034,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/text (0.76.2-0): + - React-FabricComponents/components/text (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1054,15 +1057,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/textinput (0.76.2-0): + - React-FabricComponents/components/textinput (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1077,15 +1080,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/components/unimplementedview (0.76.2-0): + - React-FabricComponents/components/unimplementedview (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1100,15 +1103,15 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricComponents/textlayoutmanager (0.76.2-0): + - React-FabricComponents/textlayoutmanager (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1123,92 +1126,68 @@ PODS: - React-rendererdebug - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon/turbomodule/core - Yoga - - React-FabricImage (0.76.2-0): + - React-FabricImage (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) - - RCTRequired (= 0.76.2-0) - - RCTTypeSafety (= 0.76.2-0) + - RCT-Folly/Fabric (= 2024.11.18.00) + - RCTRequired (= 0.77.2-0) + - RCTTypeSafety (= 0.77.2-0) - React-Fabric + - React-featureflags - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.76.2-0) + - React-jsiexecutor (= 0.77.2-0) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-featureflags (0.76.2-0) - - React-featureflagsnativemodule (0.76.2-0): - - DoubleConversion - - glog + - React-featureflags (0.77.2-0) + - React-featureflagsnativemodule (0.77.2-0): - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric + - RCT-Folly - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - Yoga - - React-graphics (0.76.2-0): + - React-graphics (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-jsi - React-jsiexecutor - React-utils - - React-hermes (0.76.2-0): + - React-hermes (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.76.2-0) + - RCT-Folly (= 2024.11.18.00) + - React-cxxreact (= 0.77.2-0) - React-jsi - - React-jsiexecutor (= 0.76.2-0) + - React-jsiexecutor (= 0.77.2-0) - React-jsinspector - - React-perflogger (= 0.76.2-0) + - React-perflogger (= 0.77.2-0) - React-runtimeexecutor - - React-idlecallbacksnativemodule (0.76.2-0): - - DoubleConversion - - glog + - React-idlecallbacksnativemodule (0.77.2-0): - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug + - RCT-Folly + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec - React-runtimescheduler - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - Yoga - - React-ImageManager (0.76.2-0): + - React-ImageManager (0.77.2-0): - glog - RCT-Folly/Fabric - React-Core/Default @@ -1217,72 +1196,62 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jserrorhandler (0.76.2-0): + - React-jserrorhandler (0.77.2-0): - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-cxxreact - React-debug + - React-featureflags - React-jsi - - React-jsi (0.76.2-0): + - ReactCommon/turbomodule/bridging + - React-jsi (0.77.2-0): - boost - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-jsiexecutor (0.76.2-0): + - RCT-Folly (= 2024.11.18.00) + - React-jsiexecutor (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-cxxreact (= 0.76.2-0) - - React-jsi (= 0.76.2-0) + - RCT-Folly (= 2024.11.18.00) + - React-cxxreact (= 0.77.2-0) + - React-jsi (= 0.77.2-0) - React-jsinspector - - React-perflogger (= 0.76.2-0) - - React-jsinspector (0.76.2-0): + - React-perflogger (= 0.77.2-0) + - React-jsinspector (0.77.2-0): - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - React-featureflags - React-jsi - - React-perflogger (= 0.76.2-0) - - React-runtimeexecutor (= 0.76.2-0) - - React-jsitracing (0.76.2-0): + - React-perflogger (= 0.77.2-0) + - React-runtimeexecutor (= 0.77.2-0) + - React-jsitracing (0.77.2-0): - React-jsi - - React-logger (0.76.2-0): + - React-logger (0.77.2-0): - glog - - React-Mapbuffer (0.76.2-0): + - React-Mapbuffer (0.77.2-0): - glog - React-debug - - React-microtasksnativemodule (0.76.2-0): - - DoubleConversion - - glog + - React-microtasksnativemodule (0.77.2-0): - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging + - RCT-Folly + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - Yoga - - react-native-video (6.12.0): + - react-native-video (6.15.0): - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1291,7 +1260,7 @@ PODS: - React-featureflags - React-graphics - React-ImageManager - - react-native-video/Video (= 6.12.0) + - react-native-video/Video (= 6.15.0) - React-NativeModulesApple - React-RCTFabric - React-rendererdebug @@ -1304,7 +1273,7 @@ PODS: - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1322,11 +1291,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-video/Video (6.12.0): + - react-native-video/Video (6.15.0): - DoubleConversion - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1343,8 +1312,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - React-nativeconfig (0.76.2-0) - - React-NativeModulesApple (0.76.2-0): + - React-nativeconfig (0.77.2-0) + - React-NativeModulesApple (0.77.2-0): - glog - hermes-engine - React-callinvoker @@ -1355,25 +1324,26 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.76.2-0): + - React-perflogger (0.77.2-0): - DoubleConversion - - RCT-Folly (= 2024.01.01.00) - - React-performancetimeline (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) + - React-performancetimeline (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - React-cxxreact + - React-featureflags - React-timing - - React-RCTActionSheet (0.76.2-0): - - React-Core/RCTActionSheetHeaders (= 0.76.2-0) - - React-RCTAnimation (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - React-RCTActionSheet (0.77.2-0): + - React-Core/RCTActionSheetHeaders (= 0.77.2-0) + - React-RCTAnimation (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTAnimationHeaders - React-jsi - React-NativeModulesApple - - ReactCodegen + - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTAppDelegate (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - React-RCTAppDelegate (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - RCTRequired - RCTTypeSafety - React-Core @@ -1387,6 +1357,7 @@ PODS: - React-nativeconfig - React-NativeModulesApple - React-RCTFabric + - React-RCTFBReactNativeSpec - React-RCTImage - React-RCTNetwork - React-rendererdebug @@ -1395,25 +1366,25 @@ PODS: - React-RuntimeHermes - React-runtimescheduler - React-utils - - ReactCodegen - ReactCommon - - React-RCTBlob (0.76.2-0): + - React-RCTBlob (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - React-Core/RCTBlobHeaders - React-Core/RCTWebSocket - React-jsi - React-jsinspector - React-NativeModulesApple + - React-RCTFBReactNativeSpec - React-RCTNetwork - - ReactCodegen - ReactCommon - - React-RCTFabric (0.76.2-0): + - React-RCTFabric (0.77.2-0): - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-Core - React-debug - React-Fabric @@ -1433,62 +1404,74 @@ PODS: - React-runtimescheduler - React-utils - Yoga - - React-RCTImage (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - React-RCTFBReactNativeSpec (0.77.2-0): + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - ReactCommon + - React-RCTImage (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTImageHeaders - React-jsi - React-NativeModulesApple + - React-RCTFBReactNativeSpec - React-RCTNetwork - - ReactCodegen - ReactCommon - - React-RCTLinking (0.76.2-0): - - React-Core/RCTLinkingHeaders (= 0.76.2-0) - - React-jsi (= 0.76.2-0) + - React-RCTLinking (0.77.2-0): + - React-Core/RCTLinkingHeaders (= 0.77.2-0) + - React-jsi (= 0.77.2-0) - React-NativeModulesApple - - ReactCodegen + - React-RCTFBReactNativeSpec - ReactCommon - - ReactCommon/turbomodule/core (= 0.76.2-0) - - React-RCTNetwork (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - ReactCommon/turbomodule/core (= 0.77.2-0) + - React-RCTNetwork (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTNetworkHeaders - React-jsi - React-NativeModulesApple - - ReactCodegen + - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTSettings (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - React-RCTSettings (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - RCTTypeSafety - React-Core/RCTSettingsHeaders - React-jsi - React-NativeModulesApple - - ReactCodegen + - React-RCTFBReactNativeSpec - ReactCommon - - React-RCTText (0.76.2-0): - - React-Core/RCTTextHeaders (= 0.76.2-0) + - React-RCTText (0.77.2-0): + - React-Core/RCTTextHeaders (= 0.77.2-0) - Yoga - - React-RCTVibration (0.76.2-0): - - RCT-Folly (= 2024.01.01.00) + - React-RCTVibration (0.77.2-0): + - RCT-Folly (= 2024.11.18.00) - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - - ReactCodegen + - React-RCTFBReactNativeSpec - ReactCommon - - React-rendererconsistency (0.76.2-0) - - React-rendererdebug (0.76.2-0): + - React-rendererconsistency (0.77.2-0) + - React-rendererdebug (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - React-debug - - React-rncore (0.76.2-0) - - React-RuntimeApple (0.76.2-0): + - React-rncore (0.77.2-0) + - React-RuntimeApple (0.77.2-0): - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-callinvoker - React-Core/Default - React-CoreModules - React-cxxreact + - React-featureflags - React-jserrorhandler - React-jsi - React-jsiexecutor @@ -1496,16 +1479,18 @@ PODS: - React-Mapbuffer - React-NativeModulesApple - React-RCTFabric + - React-RCTFBReactNativeSpec - React-RuntimeCore - React-runtimeexecutor - React-RuntimeHermes - React-runtimescheduler - React-utils - - React-RuntimeCore (0.76.2-0): + - React-RuntimeCore (0.77.2-0): - glog - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-cxxreact + - React-Fabric - React-featureflags - React-jserrorhandler - React-jsi @@ -1515,11 +1500,11 @@ PODS: - React-runtimeexecutor - React-runtimescheduler - React-utils - - React-runtimeexecutor (0.76.2-0): - - React-jsi (= 0.76.2-0) - - React-RuntimeHermes (0.76.2-0): + - React-runtimeexecutor (0.77.2-0): + - React-jsi (= 0.77.2-0) + - React-RuntimeHermes (0.77.2-0): - hermes-engine - - RCT-Folly/Fabric (= 2024.01.01.00) + - RCT-Folly/Fabric (= 2024.11.18.00) - React-featureflags - React-hermes - React-jsi @@ -1528,10 +1513,10 @@ PODS: - React-nativeconfig - React-RuntimeCore - React-utils - - React-runtimescheduler (0.76.2-0): + - React-runtimescheduler (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - React-callinvoker - React-cxxreact - React-debug @@ -1543,14 +1528,16 @@ PODS: - React-runtimeexecutor - React-timing - React-utils - - React-timing (0.76.2-0) - - React-utils (0.76.2-0): + - React-timing (0.77.2-0) + - React-utils (0.77.2-0): - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) + - RCT-Folly (= 2024.11.18.00) - React-debug - - React-jsi (= 0.76.2-0) - - ReactCodegen (0.76.2-0): + - React-jsi (= 0.77.2-0) + - ReactAppDependencyProvider (0.77.2-0): + - ReactCodegen + - ReactCodegen (0.77.2-0): - DoubleConversion - glog - hermes-engine @@ -1566,50 +1553,54 @@ PODS: - React-jsi - React-jsiexecutor - React-NativeModulesApple + - React-RCTAppDelegate - React-rendererdebug - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - ReactCommon (0.76.2-0): - - ReactCommon/turbomodule (= 0.76.2-0) - - ReactCommon/turbomodule (0.76.2-0): + - ReactCommon (0.77.2-0): + - ReactCommon/turbomodule (= 0.77.2-0) + - ReactCommon/turbomodule (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.76.2-0) - - React-cxxreact (= 0.76.2-0) - - React-jsi (= 0.76.2-0) - - React-logger (= 0.76.2-0) - - React-perflogger (= 0.76.2-0) - - ReactCommon/turbomodule/bridging (= 0.76.2-0) - - ReactCommon/turbomodule/core (= 0.76.2-0) - - ReactCommon/turbomodule/bridging (0.76.2-0): + - RCT-Folly (= 2024.11.18.00) + - React-callinvoker (= 0.77.2-0) + - React-cxxreact (= 0.77.2-0) + - React-jsi (= 0.77.2-0) + - React-logger (= 0.77.2-0) + - React-perflogger (= 0.77.2-0) + - ReactCommon/turbomodule/bridging (= 0.77.2-0) + - ReactCommon/turbomodule/core (= 0.77.2-0) + - ReactCommon/turbomodule/bridging (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.76.2-0) - - React-cxxreact (= 0.76.2-0) - - React-jsi (= 0.76.2-0) - - React-logger (= 0.76.2-0) - - React-perflogger (= 0.76.2-0) - - ReactCommon/turbomodule/core (0.76.2-0): + - RCT-Folly (= 2024.11.18.00) + - React-callinvoker (= 0.77.2-0) + - React-cxxreact (= 0.77.2-0) + - React-jsi (= 0.77.2-0) + - React-logger (= 0.77.2-0) + - React-perflogger (= 0.77.2-0) + - ReactCommon/turbomodule/core (0.77.2-0): - DoubleConversion + - fast_float (= 6.1.4) - fmt (= 9.1.0) - glog - hermes-engine - - RCT-Folly (= 2024.01.01.00) - - React-callinvoker (= 0.76.2-0) - - React-cxxreact (= 0.76.2-0) - - React-debug (= 0.76.2-0) - - React-featureflags (= 0.76.2-0) - - React-jsi (= 0.76.2-0) - - React-logger (= 0.76.2-0) - - React-perflogger (= 0.76.2-0) - - React-utils (= 0.76.2-0) + - RCT-Folly (= 2024.11.18.00) + - React-callinvoker (= 0.77.2-0) + - React-cxxreact (= 0.77.2-0) + - React-debug (= 0.77.2-0) + - React-featureflags (= 0.77.2-0) + - React-jsi (= 0.77.2-0) + - React-logger (= 0.77.2-0) + - React-perflogger (= 0.77.2-0) + - React-utils (= 0.77.2-0) - RNCPicker (2.9.0): - React-Core - SocketRocket (0.7.1) @@ -1626,6 +1617,7 @@ DEPENDENCIES: - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) - ExpoSplashScreen (from `../node_modules/expo-splash-screen/ios`) + - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) @@ -1672,6 +1664,7 @@ DEPENDENCIES: - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - React-RCTFabric (from `../node_modules/react-native/React`) + - React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`) - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) @@ -1688,6 +1681,7 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) + - ReactAppDependencyProvider (from `build/generated/ios`) - ReactCodegen (from `build/generated/ios`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" @@ -1718,6 +1712,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-modules-core" ExpoSplashScreen: :path: "../node_modules/expo-splash-screen/ios" + fast_float: + :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" FBLazyVector: :path: "../node_modules/react-native/Libraries/FBLazyVector" fmt: @@ -1726,7 +1722,7 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2024-11-12-RNv0.76.2-5b4aa20c719830dcf5684832b89a6edb95ac3d64 + :tag: hermes-2024-11-25-RNv0.77.0-d4f25d534ab744866448b36ca3bf3d97c08e638c RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -1807,6 +1803,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/Blob" React-RCTFabric: :path: "../node_modules/react-native/React" + React-RCTFBReactNativeSpec: + :path: "../node_modules/react-native/React" React-RCTImage: :path: "../node_modules/react-native/Libraries/Image" React-RCTLinking: @@ -1839,6 +1837,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/timing" React-utils: :path: "../node_modules/react-native/ReactCommon/react/utils" + ReactAppDependencyProvider: + :path: build/generated/ios ReactCodegen: :path: build/generated/ios ReactCommon: @@ -1849,81 +1849,84 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: ad4d3a971e4f0b520a127c02c8c3f98b43394c15 - DoubleConversion: 9a8708fb8299350a38a3425761aaea31263b7443 + boost: 010284190b2564d2450d97732e680dc344a9f199 + DoubleConversion: bdf45fbd16fa0d48ad8d8a56e6dc9cb1309d20e4 EXConstants: fcfc75800824ac2d5c592b5bc74130bad17b146b Expo: a6ff273c618506b12129a0d06f2a08201bfbcf43 ExpoAsset: 48386d40d53a8c1738929b3ed509bcad595b5516 ExpoFileSystem: 42d363d3b96f9afab980dcef60d5657a4443c655 ExpoFont: f354e926f8feae5e831ec8087f36652b44a0b188 ExpoKeepAwake: b0171a73665bfcefcfcc311742a72a956e6aa680 - ExpoModulesCore: 15f60d00e33ca0cea27147543877ca6e70c42ef5 + ExpoModulesCore: 7cbb8499aa4f46aeefcf332922d4ab3d4b203fa8 ExpoSplashScreen: 399ee9f85b6c8a61b965e13a1ecff8384db591c2 - FBLazyVector: 6bbb8b88508d32cdf0ffb7ceddedf6aca79eff14 + fast_float: ddaa207311c02ea1173f5343a19ba107a90b3530 + FBLazyVector: 6afb285c9285091e57d97e91a9a251794383fb9f fmt: a150d37a5595336f19c91e6f1db7fcf17d5eb99f - glog: 9ab333e271c177cecb6362fe27832c88667cb7e9 - hermes-engine: 7bc33a9a81e6a58b3bc3426105ecd59981ac730e - RCT-Folly: 839d900d8e4d2b726f3277dd085d97c8904f9eac - RCTDeprecation: b90bc991c207fa777f3d4f0d7f36d7473e62319a - RCTRequired: c8fda281396e4e5cf96e3ab216c05986fb37ca3e - RCTTypeSafety: 28a29fb79051f043943135d2e4403e77d3b7ec21 - React: dade949d9edcee964714fe90a2892beef3d3a54b - React-callinvoker: a6517e487e1645d2bc627be8d86b1f8dd5278bb5 - React-Core: 3b252d6ed5c2b9e50ed245f95aa4de1f6b5849ab - React-CoreModules: 5df4a1fa73a93ca46c33f22148b4f4693b64364e - React-cxxreact: e8b2b06dfbb9d646d9b45a29c3fc67f230e76fa9 - React-debug: 25139c51b9fda09bb1745952c81cea9f46225d6a - React-defaultsnativemodule: 6a217b48ce4eb487e7f0a9697ba6513a274cdbdc - React-domnativemodule: 64d0ffb108dc00a1275338afdfb78766fbd799d0 - React-Fabric: cb88e6fe3e97be05b425413dbba4fb6cab8e9b34 - React-FabricComponents: c5a6e9c81f9a64521ac8abf9ca2f2532e1398b86 - React-FabricImage: 724ae12ceb53b5f01dfc03d0acd63e392487713f - React-featureflags: b684bbc804b9c85754fd009167a527a6eadbd9d5 - React-featureflagsnativemodule: 46d989a753848048218ea29a30c081274c42e661 - React-graphics: accc162849d86b3a8607ce80dd49afc129f35f66 - React-hermes: 2702dda9e6550e7bfcd6d11ed2dd5ac79ee824a7 - React-idlecallbacksnativemodule: c271aad98748669cf8a045b0559a0d3fd67013d2 - React-ImageManager: 6eb6ea557f32850680896907964fc0a8515721db - React-jserrorhandler: 6236637935a035413e9ada45c7a08a2a58fa7212 - React-jsi: a18137983c0690f3e29882128aa7daf624d2fdac - React-jsiexecutor: 75909d0eeac14133f33a0446b776388591e1c7c7 - React-jsinspector: db3334448e910b60b59cd1809dcf747970525077 - React-jsitracing: 545fc999f9db67b157323f439ff964caccae2c48 - React-logger: c1804a09cb1c3486fcd9c2b4753ba7b5b9ae1c38 - React-Mapbuffer: 2c717de369c66af62d34cdf52202da3951feab59 - React-microtasksnativemodule: c285dc3fa70ea3099d8ee0887aab0fc53f3ef791 - react-native-video: b18f2dda48b6424cb8d4d6168821223bbf9eb38a - react-native-video-plugin-sample: 79af31831cddfa916eb264d89b006fdcf227a8f5 - React-nativeconfig: e90353e1c5ab46843222e25d529d342c174184f4 - React-NativeModulesApple: de38a9d30fafd773bc72e8b078d57de2dfccae7f - React-perflogger: 63dd38ee6f413be0aa176de9f6efc65819e289ca - React-performancetimeline: 450a56eff8fbe3de667f26d1857f3bd150e037a7 - React-RCTActionSheet: 0622ace751a55109844ff5d6239c83ae36f193d6 - React-RCTAnimation: 90e804d3918a97910c94732f23f1c3d3ac43d1ff - React-RCTAppDelegate: 7503e65922a7de2dcb04c7ad9dba8d18c0c1084b - React-RCTBlob: e0ac1d709fab205330a6a4ff3430b19358fe2aa4 - React-RCTFabric: a308ebf92877e49196d81568a0098bc5a54b0cc2 - React-RCTImage: 3ebf257e04f4e0d366e8d11e9ee0c9fb1d5318e4 - React-RCTLinking: 86a8f8fb5169e0499d8ff9f17b466103a9c7344f - React-RCTNetwork: 9d8dae79abf6281174aabdcd5cb952b125a19834 - React-RCTSettings: 26320c05739c8dc553e87f8195efb51fa1a4f4d1 - React-RCTText: 1b3aafa95d0b8f5546ea8b11f218b97a6c50e6ac - React-RCTVibration: b4b8c7ca82031b6a628d5197db32123786269f83 - React-rendererconsistency: abd0c952e6447de1851995630d388adb756c5490 - React-rendererdebug: d2cee4431aae3b94a369922950314bdd11ed4e36 - React-rncore: 4fa27b1f5758f1e43daecaca33828b6724ff975e - React-RuntimeApple: 4400921398bc7f37dea96b38408f75fa79a4e087 - React-RuntimeCore: f5d8891ca6c8fd0ea1ce97e1e742f674c6e3f3de - React-runtimeexecutor: 369f2c0dd457ecc25d8aca20717b8b7c655fea30 - React-RuntimeHermes: f36d89a2b787488c5c52b56f6e390ea6daad8177 - React-runtimescheduler: d767ba9dbcc56926ef8e33d9a79ffc73b1f21216 - React-timing: d1b4d886b3ad54cb7d7902551d6f326de16b13d3 - React-utils: b5263797499f58d23f8cdf84a43947fa935144a5 - ReactCodegen: be823df68a7af474545f249dab02cd94893917e8 - ReactCommon: ccfca2ddb61b208d3a9bfbbfc87233656d65a4d8 + glog: 2777ae82f2611bf0a9ed50bcd30b0eea7e7b7f05 + hermes-engine: 8eb265241fa1d7095d3a40d51fd90f7dce68217c + RCT-Folly: a3e6c7db380dfe334de6c9e7f7448093639ee88f + RCTDeprecation: b04921c77cb3dae8c42ea79054c3e6afacbe9d36 + RCTRequired: 1e6ee4199373ce214c878d8dc9eb5b1b94b93701 + RCTTypeSafety: 4926ea5e263d6185a856f073073a93d983089639 + React: 5156f6d3b295d103df09ca0ac4b4f5c0ffc60d20 + React-callinvoker: 3bc1db661750d8fcee24cdf94af0c7d298bb005d + React-Core: 456ef6b5cd74d037185d9db9d4be5b130dde5e12 + React-CoreModules: dfa2d5392f60667de20bdec2e39c957f2ff4606f + React-cxxreact: 9de89d0d4b76beeec5494599ddf7038ee99fd9d6 + React-debug: 601f2c3f4be4111ec05450b9596a5315129c3c39 + React-defaultsnativemodule: 45a7c31da5a1e2cd2c1a83d7dc6931cb18d445f3 + React-domnativemodule: 6904893a4c97e831def980ef8f4aa4ff23ca8565 + React-Fabric: 794af3fb3254ca60b72b5785da2a26337f465a8e + React-FabricComponents: af1a474eec670217dab7253db11c5faf1be3b599 + React-FabricImage: 23984800127566df46ec4b37b779d282f94df62e + React-featureflags: 84892c7e02444d384764d02c9d060909ebf295e3 + React-featureflagsnativemodule: bd150ac7e4b564f3eddc008e9cde14476fd6b2e6 + React-graphics: ef8be6f3854a9bb27c9a6108c84d976fd42dda6e + React-hermes: df4aaee5345ec3ddc702bbc1c2ce256d103e0c23 + React-idlecallbacksnativemodule: b8844569544b0f49f3ba06a43facb1b4295b1bbc + React-ImageManager: 0e4afb03f05585dbbcb4b681e7b817529af6eb31 + React-jserrorhandler: fd0d2c3d4ddbb01243a1a48de554b26a6212c08e + React-jsi: c155a66f13f3d87f3bcc8df50cadd082da768448 + React-jsiexecutor: ee45996f5a354996a85447eb4050ce569efed1dd + React-jsinspector: 03f079b9e0ea4f490940c1343c2bf548534bf383 + React-jsitracing: d87c1f38e9f6b0af79d1208e9a169e31bc3b5155 + React-logger: 173827212bd112a4312cf900a926560d02c5b761 + React-Mapbuffer: 01e52ae9359544c088445b077fcf1b9dd21f3899 + React-microtasksnativemodule: e0899c136496b4e6aa4e76c455f20842febd5571 + react-native-video: af42f681f9cb574d43fa5ab062a549600c152a2c + react-native-video-plugin-sample: 5b274863bef9c4c35541e325fdf9da28692a9fb1 + React-nativeconfig: 60d4949ebb2e752e2ccfdff4dd8796d50296125a + React-NativeModulesApple: f3c46628cd0d216cfd8f307ee4d6392da31a7de4 + React-perflogger: 89a42fbb2f343c61db60a1d8d3cf113bf91589e8 + React-performancetimeline: 16ea0022700fb601912d14f26cd5e98157c3ee26 + React-RCTActionSheet: 49af0bab1311e4867dbb883841a39fb60b685a61 + React-RCTAnimation: ed64d692480830c7fb05facfd9f0cd000d50c40e + React-RCTAppDelegate: 9ae91e667665080ae53344e617c77059056cb761 + React-RCTBlob: 1152c4f54d344127e794cf7e6be4b93fcb60c225 + React-RCTFabric: 445024d9fd87f68b2a802006e9a085b9d4dfbac4 + React-RCTFBReactNativeSpec: 3c9db6fa315eb6bb583149b4e045d1d19eb34811 + React-RCTImage: 26102f7fdf53ec797ac44b61c1e62a26fe920c5a + React-RCTLinking: 3a95c1c9d0a5022f3518c62cdc633a034acfdd7a + React-RCTNetwork: 2c1c5f8a86251fae3175000a876e87491bfa6c48 + React-RCTSettings: 59f2ac210217ece524b7cbe73c8d6bb096b8d4fe + React-RCTText: 4fc44e1b24792da42720622e2d2b45948563b86d + React-RCTVibration: e057bf4b945d8d341eca0e6cad0756bdfd3a06fd + React-rendererconsistency: cc4f0b1184157b11659dcbcffffc33437c807ee1 + React-rendererdebug: 5e81520d97505ea821a9724b77f7a2ae8bb25c0c + React-rncore: 837444670ca6585fde585fe3b208eb263c544df1 + React-RuntimeApple: 7afbe4804c9ffd7a77218adf69747edadbee53eb + React-RuntimeCore: 252450cf8b14d138acd359a490380a4a8b1ad594 + React-runtimeexecutor: 58db148db0390ec0fde94d30ba66ea254ba9d6cf + React-RuntimeHermes: 9a38783bfc459891876a973d07c6a7348faafbba + React-runtimescheduler: 4cfca15f452ad4d098d4458d4cc7d2687a016f26 + React-timing: 4bcaef266e306d2fcccfc9abbf4022123a4cb77c + React-utils: 75910cd7cf4fc3495b37cbefa5e9be114e603832 + ReactAppDependencyProvider: c441d76b9295c7260ee950f07df7c55046cd5e9f + ReactCodegen: 04e9cf3883eb4a35f8486ca835a1a67742e62667 + ReactCommon: 19520a5857e0acb26ba9213b9f65cc902c272d66 RNCPicker: 0e683a85cae99a8a739e97a49f269a4630de0a01 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 748384d41d07db337d313e0f37eec81c8c0ae46f + Yoga: bd6a143452b2ee2393c864982a5c857fafeeff97 PODFILE CHECKSUM: 90d803972b4acfc1d2bb6dbe7b0713677a2ff655 diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 975353c2..47a5d3e0 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -640,7 +640,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH DebugLog("setSrc Stopping playback") return } - self.removePlayerLayer() + + // Ensure UI operations are performed on main thread + DispatchQueue.main.sync { + self.removePlayerLayer() + } self._playerObserver.player = nil self._drmManager = nil self._playerObserver.playerItem = nil @@ -1226,14 +1230,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH if _controls != controls || ((_playerLayer == nil) && (_playerViewController == nil)) { _controls = controls if _controls { - self.removePlayerLayer() - self.usePlayerViewController() + DispatchQueue.main.async { + self.removePlayerLayer() + self.usePlayerViewController() + } } else { - _playerViewController?.view.removeFromSuperview() - _playerViewController?.removeFromParent() - _playerViewController = nil - _playerObserver.playerViewController = nil - self.usePlayerLayer() + DispatchQueue.main.async { + self._playerViewController?.view.removeFromSuperview() + self._playerViewController?.removeFromParent() + self._playerViewController = nil + self._playerObserver.playerViewController = nil + self.usePlayerLayer() + } } } }