From c6dc947000ecb7e8931252b036715daf70e877e7 Mon Sep 17 00:00:00 2001 From: Roland Kakonyi Date: Thu, 19 Oct 2023 15:02:42 +0200 Subject: [PATCH] feat(pipconfigplayerviewconfig): move PictureInPictureConfig to PlayerViewConfig --- .../player/reactnative/RNPlayerView.kt | 11 ++--- .../player/reactnative/RNPlayerViewManager.kt | 11 ++--- .../reactnative/converter/JsonConverter.kt | 6 +++ example/src/screens/BasicPictureInPicture.tsx | 26 ++++++---- ios/RCTConvert+BitmovinPlayer.swift | 47 +++++++++++++++---- ios/RNPlayerView.swift | 1 - ios/RNPlayerViewManager.swift | 11 ++--- src/components/PlayerView/index.tsx | 8 ---- src/components/PlayerView/playerViewConfig.ts | 7 +++ 9 files changed, 81 insertions(+), 47 deletions(-) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt index 42f2e77e..a649af9d 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt @@ -99,6 +99,10 @@ class RNPlayerView(val context: ReactApplicationContext) : View.OnLayoutChangeListener, RNPictureInPictureDelegate { + data class RNPlayerViewConfig( + val playerViewConfig: PlayerViewConfig?, + val pictureInPictureConfig: RNPictureInPictureHandler.PictureInPictureConfig?, + ) init { // React Native has a bug that dynamically added views sometimes aren't laid out again properly. // Since we dynamically add and remove SurfaceView under the hood this caused the player @@ -145,15 +149,10 @@ class RNPlayerView(val context: ReactApplicationContext) : */ var pictureInPictureHandler: RNPictureInPictureHandler? = null - /** - * Configuration for picture in picture behaviors. - */ - var pictureInPictureConfig: RNPictureInPictureHandler.PictureInPictureConfig? = null - /** * Configures the visual presentation and behaviour of the [playerView]. */ - var config: PlayerViewConfig? = null + var config: RNPlayerViewConfig? = null /** * Whether this view should pause video playback when activity's onPause is called. diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt index 5e8072b4..415e2495 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt @@ -183,14 +183,9 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple } } - @ReactProp(name = "pictureInPictureConfig") - fun setPictureInPictureConfig(view: RNPlayerView, pictureInPictureConfig: ReadableMap?) { - view.pictureInPictureConfig = JsonConverter.toPictureInPictureConfig(pictureInPictureConfig) - } - @ReactProp(name = "config") fun setConfig(view: RNPlayerView, config: ReadableMap?) { - view.config = if (config != null) JsonConverter.toPlayerViewConfig(config) else null + view.config = if (config != null) JsonConverter.toRNPlayerViewConfig(config) else null } private fun attachFullscreenBridge(view: RNPlayerView, fullscreenBridgeId: NativeId) { @@ -253,7 +248,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple Handler(Looper.getMainLooper()).post { val player = getPlayerModule()?.getPlayer(playerId) val playbackConfig = playerConfig?.getMap("playbackConfig") - val isPictureInPictureEnabled = view.pictureInPictureConfig?.isEnabled == true || + val isPictureInPictureEnabled = view.config?.pictureInPictureConfig?.isEnabled == true || playbackConfig?.getBooleanOrNull("isPictureInPictureEnabled") == true val pictureInPictureHandler = view.pictureInPictureHandler ?: RNPictureInPictureHandler(context) view.pictureInPictureHandler = pictureInPictureHandler @@ -270,7 +265,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple val playerView = PlayerView( currentActivity, player, - view.config ?: PlayerViewConfig(), + view.config?.playerViewConfig ?: PlayerViewConfig(), ) playerView.layoutParams = LayoutParams( LayoutParams.MATCH_PARENT, diff --git a/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt b/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt index f336ef41..7e00b8d3 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt @@ -44,6 +44,7 @@ import com.bitmovin.player.api.ui.ScalingMode import com.bitmovin.player.api.ui.StyleConfig import com.bitmovin.player.api.ui.UiConfig import com.bitmovin.player.reactnative.BitmovinCastManagerOptions +import com.bitmovin.player.reactnative.RNPlayerView import com.bitmovin.player.reactnative.extensions.getBooleanOrNull import com.bitmovin.player.reactnative.extensions.getName import com.bitmovin.player.reactnative.extensions.getOrDefault @@ -1160,6 +1161,11 @@ class JsonConverter { ?: true, ), ) + + fun toRNPlayerViewConfig(json: ReadableMap) = RNPlayerView.RNPlayerViewConfig( + playerViewConfig = toPlayerViewConfig(json), + pictureInPictureConfig = toPictureInPictureConfig(json.getMap("pictureInPictureConfig")), + ) } } diff --git a/example/src/screens/BasicPictureInPicture.tsx b/example/src/screens/BasicPictureInPicture.tsx index 5e77dd02..2b2b6975 100644 --- a/example/src/screens/BasicPictureInPicture.tsx +++ b/example/src/screens/BasicPictureInPicture.tsx @@ -7,9 +7,9 @@ import { PlayerView, SourceType, AudioSession, - PictureInPictureConfig, PictureInPictureEnterEvent, PictureInPictureExitEvent, + PlayerViewConfig, } from 'bitmovin-player-react-native'; import { useTVGestures } from '../hooks'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -34,11 +34,13 @@ export default function BasicPictureInPicture({ useState(false); const [isInPictureInPicture, setIsInPictureInPicture] = useState(false); - const pictureInPictureConfig: PictureInPictureConfig = { - // Enable picture in picture UI option on player controls. - isEnabled: true, - // Enable entering picture in picture mode when transitioning the application to the background - shouldEnterOnBackground: true, + const config: PlayerViewConfig = { + pictureInPictureConfig: { + // Enable picture in picture UI option on player controls. + isEnabled: true, + // Enable entering picture in picture mode when transitioning the application to the background + shouldEnterOnBackground: true, + }, }; const player = usePlayer({ @@ -115,12 +117,20 @@ export default function BasicPictureInPicture({ ); return ( - + RNPlayerViewConfig? { - guard let json = json as? [String: Any?], - let uiConfigJson = json["uiConfig"] as? [String: Any?] else { + static func rnPlayerViewConfig(_ json: Any?) -> RNPlayerViewConfig? { + guard let json = json as? [String: Any?] else { return nil } return RNPlayerViewConfig( - uiConfig: UiConfig( - playbackSpeedSelectionEnabled: uiConfigJson["playbackSpeedSelectionEnabled"] as? Bool ?? true - ) + uiConfig: rnUiConfig(json["uiConfig"]), + pictureInPictureConfig: pictureInPictureConfig(json["pictureInPictureConfig"]) + ) + } + + /** + Utility method to instantiate a `RNUiConfig` from a JS object. + - Parameter json: JS object + - Returns: The produced `RNUiConfig` object + */ + static func rnUiConfig(_ json: Any?) -> RNUiConfig? { + guard let json = json as? [String: Any?] else { + return nil + } + + return RNUiConfig( + playbackSpeedSelectionEnabled: json["playbackSpeedSelectionEnabled"] as? Bool ?? true ) } } @@ -1146,12 +1159,28 @@ internal struct RNPlayerViewConfig { /** * The react native specific ui configuration. */ - let uiConfig: UiConfig + let uiConfig: RNUiConfig? + + /** + * Picture in picture config + */ + let pictureInPictureConfig: PictureInPictureConfig? + + /** + * PlayerView config considering all properties + */ + var playerViewConfig: PlayerViewConfig { + let config = PlayerViewConfig() + if let pictureInPictureConfig { + config.pictureInPictureConfig = pictureInPictureConfig + } + return config + } } /** * React native specific UiConfig. */ -internal struct UiConfig { +internal struct RNUiConfig { let playbackSpeedSelectionEnabled: Bool } diff --git a/ios/RNPlayerView.swift b/ios/RNPlayerView.swift index b196614d..b4f0e1b2 100644 --- a/ios/RNPlayerView.swift +++ b/ios/RNPlayerView.swift @@ -63,7 +63,6 @@ public class RNPlayerView: UIView { @objc var onCastTimeUpdated: RCTBubblingEventBlock? @objc var onCastWaitingForDevice: RCTBubblingEventBlock? @objc var onPictureInPictureAvailabilityChanged: RCTBubblingEventBlock? - @objc var pictureInPictureConfig: [String: Any]? @objc var config: [String: Any]? /// The `PlayerView` subview. diff --git a/ios/RNPlayerViewManager.swift b/ios/RNPlayerViewManager.swift index b5a7f411..89b72ca0 100644 --- a/ios/RNPlayerViewManager.swift +++ b/ios/RNPlayerViewManager.swift @@ -31,6 +31,7 @@ public class RNPlayerViewManager: RCTViewManager { else { return } + let playerViewConfig = RCTConvert.rnPlayerViewConfig(view.config) #if os(iOS) if player.config.styleConfig.userInterfaceType == .bitmovin { let bitmovinUserInterfaceConfig = player @@ -38,9 +39,9 @@ public class RNPlayerViewManager: RCTViewManager { .styleConfig .userInterfaceConfig as? BitmovinUserInterfaceConfig ?? BitmovinUserInterfaceConfig() player.config.styleConfig.userInterfaceConfig = bitmovinUserInterfaceConfig - if let config = RCTConvert.playerViewConfig(view.config) { + if let uiConfig = playerViewConfig?.uiConfig { bitmovinUserInterfaceConfig - .playbackSpeedSelectionEnabled = config.uiConfig.playbackSpeedSelectionEnabled + .playbackSpeedSelectionEnabled = uiConfig.playbackSpeedSelectionEnabled } if let customMessageHandlerBridgeId = self.customMessageHandlerBridgeId, @@ -56,14 +57,10 @@ public class RNPlayerViewManager: RCTViewManager { playerView.player = player previousPictureInPictureAvailableValue = playerView.isPictureInPictureAvailable } else { - let playerViewConfig = PlayerViewConfig() - if let pictureInPictureConfig = RCTConvert.pictureInPictureConfig(view.pictureInPictureConfig) { - playerViewConfig.pictureInPictureConfig = pictureInPictureConfig - } view.playerView = PlayerView( player: player, frame: view.bounds, - playerViewConfig: playerViewConfig + playerViewConfig: playerViewConfig?.playerViewConfig ?? PlayerViewConfig() ) previousPictureInPictureAvailableValue = false } diff --git a/src/components/PlayerView/index.tsx b/src/components/PlayerView/index.tsx index eb482dcb..230bbe7f 100644 --- a/src/components/PlayerView/index.tsx +++ b/src/components/PlayerView/index.tsx @@ -15,7 +15,6 @@ import { FullscreenHandler, CustomMessageHandler } from '../../ui'; import { FullscreenHandlerBridge } from '../../ui/fullscreenhandlerbridge'; import { CustomMessageHandlerBridge } from '../../ui/custommessagehandlerbridge'; import { ScalingMode } from '../../styleConfig'; -import { PictureInPictureConfig } from './pictureInPictureConfig'; import { PlayerViewConfig } from './playerViewConfig'; /** @@ -38,11 +37,6 @@ export interface BasePlayerViewProps { */ style?: ViewStyle; - /** - * Provides options to configure Picture in Picture playback. - */ - pictureInPictureConfig?: PictureInPictureConfig; - /** * Configures the visual presentation and behaviour of the `PlayerView`. * The value must not be altered after setting it initially. @@ -123,7 +117,6 @@ export function PlayerView({ isFullscreenRequested = false, scalingMode, isPictureInPictureRequested = false, - pictureInPictureConfig, ...props }: PlayerViewProps) { // Workaround React Native UIManager commands not sent until UI refresh @@ -223,7 +216,6 @@ export function PlayerView({ style={nativeViewStyle} fullscreenBridge={fullscreenBridge.current} customMessageHandlerBridge={customMessageHandlerBridge.current} - pictureInPictureConfig={pictureInPictureConfig} config={config} onAdBreakFinished={proxy(props.onAdBreakFinished)} onAdBreakStarted={proxy(props.onAdBreakStarted)} diff --git a/src/components/PlayerView/playerViewConfig.ts b/src/components/PlayerView/playerViewConfig.ts index 15f9584b..cd7fdcd4 100644 --- a/src/components/PlayerView/playerViewConfig.ts +++ b/src/components/PlayerView/playerViewConfig.ts @@ -1,3 +1,5 @@ +import { PictureInPictureConfig } from './pictureInPictureConfig'; + /** * Configures the visual presentation and behaviour of the `PlayerView`. */ @@ -12,6 +14,11 @@ export interface PlayerViewConfig { * Configuring the `uiConfig` only has an effect if the {@link StyleConfig.userInterfaceType} is set to {@link UserInterfaceType.Bitmovin}. */ uiConfig: UiConfig; + + /** + * Provides options to configure Picture in Picture playback. + */ + pictureInPictureConfig?: PictureInPictureConfig; } /**