fix(android): add subtitleStyle.subtitlesFollowVideo prop to control subtitles positionning (#4133)

* fix(android): add subtitleStyle.subtitlesFollowVideo prop to control subtitles positionning
* docs: add new prop description
* docs: add supported platform for subtitleStyle
* chore: use constructor instead of parse
This commit is contained in:
Olivier Bouillet 2024-09-02 16:13:06 +02:00 committed by GitHub
parent 688d98d68f
commit 2fa6c43615
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 13 deletions

View File

@ -6,7 +6,7 @@ import com.facebook.react.bridge.ReadableMap
/**
* Helper file to parse SubtitleStyle prop and build a dedicated class
*/
class SubtitleStyle private constructor() {
class SubtitleStyle public constructor() {
var fontSize = -1
private set
var paddingLeft = 0
@ -19,6 +19,8 @@ class SubtitleStyle private constructor() {
private set
var opacity = 1f
private set
var subtitlesFollowVideo = true
private set
companion object {
private const val PROP_FONT_SIZE_TRACK = "fontSize"
@ -27,6 +29,7 @@ class SubtitleStyle private constructor() {
private const val PROP_PADDING_LEFT = "paddingLeft"
private const val PROP_PADDING_RIGHT = "paddingRight"
private const val PROP_OPACITY = "opacity"
private const val PROP_SUBTITLES_FOLLOW_VIDEO = "subtitlesFollowVideo"
@JvmStatic
fun parse(src: ReadableMap?): SubtitleStyle {
@ -37,6 +40,7 @@ class SubtitleStyle private constructor() {
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)
subtitleStyle.subtitlesFollowVideo = ReactBridgeUtils.safeGetBool(src, PROP_SUBTITLES_FOLLOW_VIDEO, true)
return subtitleStyle
}
}

View File

@ -48,6 +48,8 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private @ViewType.ViewType int viewType = ViewType.VIEW_TYPE_SURFACE;
private boolean hideShutterView = false;
private SubtitleStyle localStyle = new SubtitleStyle();
public ExoPlayerView(Context context) {
super(context, null, 0);
@ -80,11 +82,16 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
adOverlayFrameLayout = new FrameLayout(context);
layout.addView(shutterView, 1, layoutParams);
layout.addView(adOverlayFrameLayout, 2, layoutParams);
if (localStyle.getSubtitlesFollowVideo()) {
layout.addView(subtitleLayout, layoutParams);
layout.addView(adOverlayFrameLayout, layoutParams);
}
addViewInLayout(layout, 0, aspectRatioParams);
if (!localStyle.getSubtitlesFollowVideo()) {
addViewInLayout(subtitleLayout, 1, layoutParams);
}
}
private void clearVideoView() {
if (surfaceView instanceof TextureView) {
@ -107,7 +114,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
}
public void setSubtitleStyle(SubtitleStyle style) {
// ensure we reset subtile style before reapplying it
// ensure we reset subtitle style before reapplying it
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();
@ -121,7 +128,18 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
} else {
subtitleLayout.setVisibility(View.GONE);
}
if (localStyle.getSubtitlesFollowVideo() != style.getSubtitlesFollowVideo()) {
// No need to manipulate layout if value didn't change
if (style.getSubtitlesFollowVideo()) {
removeViewInLayout(subtitleLayout);
layout.addView(subtitleLayout, layoutParams);
} else {
layout.removeViewInLayout(subtitleLayout);
addViewInLayout(subtitleLayout, 1, layoutParams, false);
}
requestLayout();
}
localStyle = style;
}
public void setShutterColor(Integer color) {

View File

@ -846,14 +846,18 @@ source={{
### `subtitleStyle`
| Property | Description | Platforms |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| fontSize | Adjust the font size of the subtitles. Default: font size of the device | Android |
| paddingTop | Adjust the top 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 |
| 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 |
<PlatformsList types={['Android', 'iOS']} />
| Property | Platform | Description | Platforms |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| fontSize | Android | Adjust the font size of the subtitles. Default: font size of the device | Android |
| paddingTop | Android | Adjust the top padding of the subtitles. Default: 0 | Android |
| paddingBottom | Android | Adjust the bottom padding of the subtitles. Default: 0 | Android |
| paddingLeft | Android | Adjust the left padding of the subtitles. Default: 0 | Android |
| paddingRight | Android | Adjust the right padding of the subtitles. Default: 0 | Android |
| opacity | Android, iOS | 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 |
| subtitlesFollowVideo | Android | Boolean to adjust position of subtitles. Default: true |
Example:
@ -861,6 +865,27 @@ Example:
subtitleStyle={{ paddingBottom: 50, fontSize: 20, opacity: 0 }}
```
Note for `subtitlesFollowVideo`
`subtitlesFollowVideo` helps to determine how the subtitles are positionned.
To understand this prop you need to understand how views management works.
The main View style passed to react native video is the position reserved to display the video component.
It may not match exactly the real video size.
For exemple, you can pass a 4:3 video view and render a 16:9 video inside.
So there is a second view, the video view.
Subtitles are managed in a third view.
First react-native-video resize the video to keep aspect ratio (depending on `resizeMode` property) and put it in main view.
* When putting subtitlesFollowVideo to true, the subtitle view will be adapt to the video view.
It means that if the video is displayed out of screen, the subtitles may also be displayed out of screen.
* When putting subtitlesFollowVideo to false, the subtitle view will keep adapting to the main view.
It means that if the video is displayed out of screen, the subtitles may also be displayed out of screen.
This prop can be changed on runtime.
### `textTracks`
<PlatformsList types={['Android', 'iOS', 'visionOS']} />

View File

@ -268,6 +268,7 @@ const VideoPlayer: FC<Props> = ({}) => {
onPlaybackStateChanged={onPlaybackStateChanged}
bufferingStrategy={BufferingStrategyType.DEFAULT}
debug={{enable: true, thread: true}}
subtitleStyle={{subtitlesFollowVideo: true}}
/>
</TouchableOpacity>
)}

View File

@ -131,6 +131,7 @@ type SubtitleStyle = Readonly<{
paddingLeft?: WithDefault<Float, 0>;
paddingRight?: WithDefault<Float, 0>;
opacity?: WithDefault<Float, 1>;
subtitlesFollowVideo?: WithDefault<boolean, true>;
}>;
type OnLoadData = Readonly<{

View File

@ -168,6 +168,7 @@ export type SubtitleStyle = {
paddingLeft?: number;
paddingRight?: number;
opacity?: number;
subtitlesFollowVideo?: boolean;
};
export enum TextTrackType {