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>
This commit is contained in:
parent
41ac781412
commit
f4cce2ecdb
@ -17,6 +17,8 @@ class SubtitleStyle private constructor() {
|
|||||||
private set
|
private set
|
||||||
var paddingBottom = 0
|
var paddingBottom = 0
|
||||||
private set
|
private set
|
||||||
|
var opacity = 1f
|
||||||
|
private set
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val PROP_FONT_SIZE_TRACK = "fontSize"
|
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_TOP = "paddingTop"
|
||||||
private const val PROP_PADDING_LEFT = "paddingLeft"
|
private const val PROP_PADDING_LEFT = "paddingLeft"
|
||||||
private const val PROP_PADDING_RIGHT = "paddingRight"
|
private const val PROP_PADDING_RIGHT = "paddingRight"
|
||||||
|
private const val PROP_OPACITY = "opacity"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parse(src: ReadableMap?): SubtitleStyle {
|
fun parse(src: ReadableMap?): SubtitleStyle {
|
||||||
@ -33,6 +36,7 @@ class SubtitleStyle private constructor() {
|
|||||||
subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0)
|
subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0)
|
||||||
subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0)
|
subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0)
|
||||||
subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0)
|
subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0)
|
||||||
|
subtitleStyle.opacity = ReactBridgeUtils.safeGetFloat(src, PROP_OPACITY, 1f)
|
||||||
return subtitleStyle
|
return subtitleStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,14 @@ object ReactBridgeUtils {
|
|||||||
return safeGetDouble(map, key, 0.0)
|
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.
|
* toStringMap converts a [ReadableMap] into a HashMap.
|
||||||
*
|
*
|
||||||
|
@ -117,6 +117,13 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize());
|
subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize());
|
||||||
}
|
}
|
||||||
subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom());
|
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) {
|
public void setShutterColor(Integer color) {
|
||||||
|
@ -534,6 +534,8 @@ Example:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For details on how to control the visibility of subtitles, see the [subtitleStyle](./props.mdx#subtitleStyle) section.
|
||||||
|
|
||||||
### `onVideoTracks`
|
### `onVideoTracks`
|
||||||
|
|
||||||
<PlatformsList types={['Android']} />
|
<PlatformsList types={['Android']} />
|
||||||
|
@ -748,11 +748,12 @@ source={{
|
|||||||
| paddingBottom | Adjust the bottom padding of the subtitles. Default: 0 | Android |
|
| paddingBottom | Adjust the bottom padding of the subtitles. Default: 0 | Android |
|
||||||
| paddingLeft | Adjust the left 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 |
|
| 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:
|
Example:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
subtitleStyle={{ paddingBottom: 50, fontSize: 20 }}
|
subtitleStyle={{ paddingBottom: 50, fontSize: 20, opacity: 0 }}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `textTracks`
|
### `textTracks`
|
||||||
|
17
ios/Video/DataStructures/SubtitleStyle.swift
Normal file
17
ios/Video/DataStructures/SubtitleStyle.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,8 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var subtitleStyle: SubtitleStyle?
|
||||||
|
|
||||||
var playerItem: AVPlayerItem? {
|
var playerItem: AVPlayerItem? {
|
||||||
willSet {
|
willSet {
|
||||||
removePlayerItemObservers()
|
removePlayerItemObservers()
|
||||||
@ -63,6 +65,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla
|
|||||||
playerItem.add(legibleOutput)
|
playerItem.add(legibleOutput)
|
||||||
metadataOutput.setDelegate(self, queue: .main)
|
metadataOutput.setDelegate(self, queue: .main)
|
||||||
legibleOutput.setDelegate(self, queue: .main)
|
legibleOutput.setDelegate(self, queue: .main)
|
||||||
|
legibleOutput.suppressesPlayerRendering = subtitleStyle?.opacity == 0 ? true : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,6 +953,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
_playerObserver.playerLayer = nil
|
_playerObserver.playerLayer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func setSubtitleStyle(_ style: [String: Any]) {
|
||||||
|
let subtitleStyle = SubtitleStyle.parse(from: style)
|
||||||
|
_playerObserver.subtitleStyle = subtitleStyle
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - RCTVideoPlayerViewControllerDelegate
|
// MARK: - RCTVideoPlayerViewControllerDelegate
|
||||||
|
|
||||||
func videoPlayerViewControllerWillDismiss(playerViewController: AVPlayerViewController) {
|
func videoPlayerViewControllerWillDismiss(playerViewController: AVPlayerViewController) {
|
||||||
|
@ -37,7 +37,7 @@ RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(subtitleStyle, NSDictionary);
|
||||||
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock);
|
||||||
|
@ -118,6 +118,7 @@ type SubtitleStyle = Readonly<{
|
|||||||
paddingBottom?: WithDefault<Float, 0>;
|
paddingBottom?: WithDefault<Float, 0>;
|
||||||
paddingLeft?: WithDefault<Float, 0>;
|
paddingLeft?: WithDefault<Float, 0>;
|
||||||
paddingRight?: WithDefault<Float, 0>;
|
paddingRight?: WithDefault<Float, 0>;
|
||||||
|
opacity?: WithDefault<Float, 1>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type OnLoadData = Readonly<{
|
export type OnLoadData = Readonly<{
|
||||||
|
@ -104,6 +104,7 @@ export type SubtitleStyle = {
|
|||||||
paddingBottom?: number;
|
paddingBottom?: number;
|
||||||
paddingLeft?: number;
|
paddingLeft?: number;
|
||||||
paddingRight?: number;
|
paddingRight?: number;
|
||||||
|
opacity?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum TextTracksType {
|
export enum TextTracksType {
|
||||||
|
Loading…
Reference in New Issue
Block a user