From f4cce2ecdba0668c3ecf74d2fd7956df4dd8489d Mon Sep 17 00:00:00 2001 From: coofzilla <75731066+coofzilla@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:29:50 +0900 Subject: [PATCH] feat: implement opacity to control visibility of subtitles (#3583) * feat: implement opacity to control visibility of subtitles implemented per discussion on https://github.com/react-native-video/react-native-video/issues/3579 updated docs and linked onTextTrackDataChanged to the subtitle style to clarify intent on how to control visibility. * chore: update type so that we use a union of 0 1 vs any number * chore: run ktlint to get rid of white spaces * feat: add ability to have range of numbers for opacity; while, 0 will still not render the subtitles. added util function for safeGetFloat updated types * feat: add ability to suppress subtitles with opacity 0 add data structure for subtitle styles for extensibility * chore: run yarn check-ios * chore: update documentation to clarify differences between android and ios * Update android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> --------- Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> --- .../com/brentvatne/common/api/SubtitleStyle.kt | 4 ++++ .../common/toolbox/ReactBridgeUtils.kt | 8 ++++++++ .../com/brentvatne/exoplayer/ExoPlayerView.java | 7 +++++++ docs/pages/component/events.mdx | 2 ++ docs/pages/component/props.mdx | 3 ++- ios/Video/DataStructures/SubtitleStyle.swift | 17 +++++++++++++++++ ios/Video/Features/RCTPlayerObserver.swift | 3 +++ ios/Video/RCTVideo.swift | 6 ++++++ ios/Video/RCTVideoManager.m | 2 +- src/specs/VideoNativeComponent.ts | 1 + src/types/video.ts | 1 + 11 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ios/Video/DataStructures/SubtitleStyle.swift diff --git a/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt b/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt index 1e32c77ada..efb4da0a81 100644 --- a/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt +++ b/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt @@ -17,6 +17,8 @@ class SubtitleStyle private constructor() { private set var paddingBottom = 0 private set + var opacity = 1f + private set companion object { private const val PROP_FONT_SIZE_TRACK = "fontSize" @@ -24,6 +26,7 @@ class SubtitleStyle private constructor() { private const val PROP_PADDING_TOP = "paddingTop" private const val PROP_PADDING_LEFT = "paddingLeft" private const val PROP_PADDING_RIGHT = "paddingRight" + private const val PROP_OPACITY = "opacity" @JvmStatic fun parse(src: ReadableMap?): SubtitleStyle { @@ -33,6 +36,7 @@ class SubtitleStyle private constructor() { subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0) subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0) subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0) + subtitleStyle.opacity = ReactBridgeUtils.safeGetFloat(src, PROP_OPACITY, 1f) return subtitleStyle } } diff --git a/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt b/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt index 65dc57a5b4..c2ee24921a 100644 --- a/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt +++ b/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt @@ -66,6 +66,14 @@ object ReactBridgeUtils { return safeGetDouble(map, key, 0.0) } + @JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?, fallback: Float): Float { + return if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getDouble(key).toFloat() else fallback + } + + @JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?): Float { + return safeGetFloat(map, key, 0.0f) + } + /** * toStringMap converts a [ReadableMap] into a HashMap. * diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index b00b4e9564..ebe05d16a9 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -117,6 +117,13 @@ public void setSubtitleStyle(SubtitleStyle style) { subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize()); } subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom()); + if (style.getOpacity() != 0) { + subtitleLayout.setAlpha(style.getOpacity()); + subtitleLayout.setVisibility(View.VISIBLE); + } else { + subtitleLayout.setVisibility(View.GONE); + } + } public void setShutterColor(Integer color) { diff --git a/docs/pages/component/events.mdx b/docs/pages/component/events.mdx index fab327f884..09d50a2944 100644 --- a/docs/pages/component/events.mdx +++ b/docs/pages/component/events.mdx @@ -534,6 +534,8 @@ Example: } ``` +For details on how to control the visibility of subtitles, see the [subtitleStyle](./props.mdx#subtitleStyle) section. + ### `onVideoTracks` diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index cb87d46315..e3c7986d63 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -748,11 +748,12 @@ source={{ | paddingBottom | Adjust the bottom padding of the subtitles. Default: 0 | Android | | paddingLeft | Adjust the left padding of the subtitles. Default: 0 | Android | | paddingRight | Adjust the right padding of the subtitles. Default: 0 | Android | +| opacity | Adjust the visibility of subtitles with 0 hiding and 1 fully showing them. Android supports float values between 0 and 1 for varying opacity levels, whereas iOS supports only 0 or 1. Default: 1. | Android, iOS | Example: ```javascript -subtitleStyle={{ paddingBottom: 50, fontSize: 20 }} +subtitleStyle={{ paddingBottom: 50, fontSize: 20, opacity: 0 }} ``` ### `textTracks` diff --git a/ios/Video/DataStructures/SubtitleStyle.swift b/ios/Video/DataStructures/SubtitleStyle.swift new file mode 100644 index 0000000000..76bd8632c8 --- /dev/null +++ b/ios/Video/DataStructures/SubtitleStyle.swift @@ -0,0 +1,17 @@ +struct SubtitleStyle { + // Extend with more style properties as needed. + private(set) var opacity: CGFloat + + enum SubtitleStyleKeys { + static let opacity = "opacity" + } + + init(opacity: CGFloat = 1) { + self.opacity = opacity + } + + static func parse(from dictionary: [String: Any]?) -> SubtitleStyle { + let opacity = dictionary?[SubtitleStyleKeys.opacity] as? CGFloat ?? 1 + return SubtitleStyle(opacity: opacity) + } +} diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index c1c93262f2..22db23a8fe 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -47,6 +47,8 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla } } + var subtitleStyle: SubtitleStyle? + var playerItem: AVPlayerItem? { willSet { removePlayerItemObservers() @@ -63,6 +65,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla playerItem.add(legibleOutput) metadataOutput.setDelegate(self, queue: .main) legibleOutput.setDelegate(self, queue: .main) + legibleOutput.suppressesPlayerRendering = subtitleStyle?.opacity == 0 ? true : false } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 2c051cf0cd..7ff053eb56 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -953,6 +953,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _playerObserver.playerLayer = nil } + @objc + func setSubtitleStyle(_ style: [String: Any]) { + let subtitleStyle = SubtitleStyle.parse(from: style) + _playerObserver.subtitleStyle = subtitleStyle + } + // MARK: - RCTVideoPlayerViewControllerDelegate func videoPlayerViewControllerWillDismiss(playerViewController: AVPlayerViewController) { diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index e30f6c9cb5..de1140ef16 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -37,7 +37,7 @@ @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL); RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString); - +RCT_EXPORT_VIEW_PROPERTY(subtitleStyle, NSDictionary); /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock); diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 718ffbfc23..6d718b8d28 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -118,6 +118,7 @@ type SubtitleStyle = Readonly<{ paddingBottom?: WithDefault; paddingLeft?: WithDefault; paddingRight?: WithDefault; + opacity?: WithDefault; }>; export type OnLoadData = Readonly<{ diff --git a/src/types/video.ts b/src/types/video.ts index 6fa2841e37..5f505118b0 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -104,6 +104,7 @@ export type SubtitleStyle = { paddingBottom?: number; paddingLeft?: number; paddingRight?: number; + opacity?: number; }; export enum TextTracksType {