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 {