Skip to content

Commit

Permalink
Merge pull request #546 from bitmovin/PRN-106/include-smart-tv-ui-for…
Browse files Browse the repository at this point in the history
…-react-native-android-tv-os

Include the possibility to define uiManagerFactoryFunction
  • Loading branch information
zigavehovec authored Oct 31, 2024
2 parents 925e537 + 7b286cf commit f2f0c64
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 12 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
- Update Bitmovin's native Android SDK version to `3.90.0`
- Update Bitmovin's native iOS SDK version to `3.76.0`

### Added

- `WebUiConfig.variant` to set the UI variant that should be used by the Bitmovin Web UI

### Fixed

- Spatial navigation in the Web UI does not work properly

## [0.29.0] - 2024-09-09

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,18 +752,33 @@ fun ReadableMap.toPictureInPictureConfig(): PictureInPictureConfig = PictureInPi
isEnabled = getBooleanOrNull("isEnabled") ?: false,
)

/**
* Converts the [json] to a `RNUiConfig` object.
*/
fun toPlayerViewConfig(json: ReadableMap) = PlayerViewConfig(
uiConfig = UiConfig.WebUi(
playbackSpeedSelectionEnabled = json.getMap("uiConfig")
?.getBooleanOrNull("playbackSpeedSelectionEnabled")
?: true,
),
hideFirstFrame = json.getBooleanOrNull("hideFirstFrame") ?: false,
fun ReadableMap.toPlayerViewConfig(): PlayerViewConfig = PlayerViewConfig(
uiConfig = getMap("uiConfig")?.toUiConfig() ?: UiConfig.WebUi(),
hideFirstFrame = getBooleanOrNull("hideFirstFrame") ?: false,
)

private fun ReadableMap.toUiConfig(): UiConfig {
val variant = toVariant() ?: UiConfig.WebUi.Variant.SmallScreenUi
val focusUiOnInitialization = getBooleanOrNull("focusUiOnInitialization")
val defaultFocusUiOnInitialization = variant == UiConfig.WebUi.Variant.TvUi

return UiConfig.WebUi(
playbackSpeedSelectionEnabled = getBooleanOrNull("playbackSpeedSelectionEnabled") ?: true,
variant = variant,
focusUiOnInitialization = focusUiOnInitialization ?: defaultFocusUiOnInitialization,
)
}

private fun ReadableMap.toVariant(): UiConfig.WebUi.Variant? {
val uiManagerFactoryFunction = getMap("variant")?.getString("uiManagerFactoryFunction") ?: return null

return when (uiManagerFactoryFunction) {
"bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI" -> UiConfig.WebUi.Variant.SmallScreenUi
"bitmovin.playerui.UIFactory.buildDefaultTvUI" -> UiConfig.WebUi.Variant.TvUi
else -> UiConfig.WebUi.Variant.Custom(uiManagerFactoryFunction)
}
}

private fun ReadableMap.toUserInterfaceTypeFromPlayerConfig(): UserInterfaceType? =
when (getMap("styleConfig")?.getString("userInterfaceType")) {
"Subtitle" -> UserInterfaceType.Subtitle
Expand All @@ -775,7 +790,7 @@ private fun ReadableMap.toUserInterfaceTypeFromPlayerConfig(): UserInterfaceType
* Converts the [this@toRNPlayerViewConfigWrapper] to a `RNPlayerViewConfig` object.
*/
fun ReadableMap.toRNPlayerViewConfigWrapper() = RNPlayerViewConfigWrapper(
playerViewConfig = toPlayerViewConfig(this),
playerViewConfig = toPlayerViewConfig(),
pictureInPictureConfig = getMap("pictureInPictureConfig")?.toPictureInPictureConfig(),
)

Expand Down
10 changes: 10 additions & 0 deletions example/src/screens/BasicPlayback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
usePlayer,
PlayerView,
SourceType,
TvUi,
SmallScreenUi,
PlayerViewConfig,
} from 'bitmovin-player-react-native';
import { useTVGestures } from '../hooks';

Expand All @@ -22,6 +25,12 @@ export default function BasicPlayback() {
},
});

const config: PlayerViewConfig = {
uiConfig: {
variant: Platform.isTV ? new TvUi() : new SmallScreenUi(),
},
};

useFocusEffect(
useCallback(() => {
player.load({
Expand Down Expand Up @@ -56,6 +65,7 @@ export default function BasicPlayback() {
<PlayerView
player={player}
style={styles.player}
config={config}
onPlay={onEvent}
onPlaying={onEvent}
onPaused={onEvent}
Expand Down
7 changes: 6 additions & 1 deletion ios/RCTConvert+BitmovinPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1239,9 +1239,13 @@ extension RCTConvert {
guard let json = json as? [String: Any?] else {
return nil
}
let variant = json["variant"] as? [String: Any?]
let uiManagerFactoryFunction = variant?["uiManagerFactoryFunction"] as? String
let defaultUiManagerFactoryFunction = "bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI"

return RNUiConfig(
playbackSpeedSelectionEnabled: json["playbackSpeedSelectionEnabled"] as? Bool ?? true
playbackSpeedSelectionEnabled: json["playbackSpeedSelectionEnabled"] as? Bool ?? true,
uiManagerFactoryFunction: uiManagerFactoryFunction ?? defaultUiManagerFactoryFunction
)
}

Expand Down Expand Up @@ -1369,6 +1373,7 @@ internal struct RNPlayerViewConfig {
*/
internal struct RNUiConfig {
let playbackSpeedSelectionEnabled: Bool
let uiManagerFactoryFunction: String
}

/**
Expand Down
1 change: 1 addition & 0 deletions ios/RNPlayerViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class RNPlayerViewManager: RCTViewManager {
if let uiConfig = playerViewConfig?.uiConfig {
bitmovinUserInterfaceConfig
.playbackSpeedSelectionEnabled = uiConfig.playbackSpeedSelectionEnabled
bitmovinUserInterfaceConfig.uiManagerFactoryFunction = uiConfig.uiManagerFactoryFunction
}
if let hideFirstFrame = playerViewConfig?.hideFirstFrame {
bitmovinUserInterfaceConfig.hideFirstFrame = hideFirstFrame
Expand Down
53 changes: 53 additions & 0 deletions src/components/PlayerView/playerViewConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,57 @@ export interface WebUiConfig extends UiConfig {
* Default is `true`.
*/
playbackSpeedSelectionEnabled?: boolean;
/**
* The UI variant to use for the Bitmovin Player Web UI.
*
* Default is {@link SmallScreenUi}
*/
variant?: Variant;
/**
* Whether the WebView should be focused on initialization.
*
* By default this is enabled only for the TV UI variant, as it's needed there to
* initiate spatial navigation using the remote control.
*
* @platform Android
*/
focusUiOnInitialization?: boolean;
}

export abstract class Variant {
/**
* Specifies the function name that will be used to initialize the `UIManager`
* for the Bitmovin Player Web UI.
*
* The function is called on the `window` object with the `Player` as the first argument and
* the `UIConfig` as the second argument.
*
* Example:
* When you added a new function or want to use a different function of our `UIFactory`,
* you can specify the full qualifier name including namespaces.
* e.g. `bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI` for the SmallScreenUi.
* @see UIFactory https://github.com/bitmovin/bitmovin-player-ui/blob/develop/src/ts/uifactory.ts#L60
*
* Notes:
* - It's not necessary to use our `UIFactory`. Any static function can be specified.
*/
constructor(public readonly uiManagerFactoryFunction: string) {}
}

export class SmallScreenUi extends Variant {
constructor() {
super('bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI');
}
}

export class TvUi extends Variant {
constructor() {
super('bitmovin.playerui.UIFactory.buildDefaultTvUI');
}
}

export class CustomUi extends Variant {
constructor(uiManagerFactoryFunction: string) {
super(uiManagerFactoryFunction);
}
}

0 comments on commit f2f0c64

Please sign in to comment.