Hi @anon63174557, yes bellow you will find the layout that host the PlayerView which is basically pretty much similar to the TakeOffMedia’s component:
import React, { useEffect, useImperativeHandle, useRef, useState } from "react";
import {
LayoutRectangle,
NativeModules,
Platform,
View,
findNodeHandle,
requireNativeComponent,
} from "react-native";
const BitmovinVideoPlayerModule = NativeModules.BGBitmovinVideoPlayerModule;
export type ReactNativeBitmovinPlayerMethodsType = {
play(): void;
pause(): void;
destroy(): void;
};
type BitmovinVideoPlayerType = {
autoPlay: boolean;
onReady?: (event: unknown) => void;
onEnterFullscreen: (event: unknown) => void;
configuration: {
url: string;
thumbnail?: string;
title?: string;
style?: {
uiEnabled?: boolean;
fullscreenIcon?: boolean;
};
};
drmConfig?: {
licenseUrl?: string | undefined;
headers?: string | undefined;
token?: string | undefined;
};
style?: any;
};
const BitmovinVideoPlayer = requireNativeComponent<BitmovinVideoPlayerType>(
"BGBitmovinVideoPlayerManager",
);
export default React.forwardRef<
ReactNativeBitmovinPlayerMethodsType,
BitmovinVideoPlayerType
>(
(
{
configuration,
onEnterFullscreen,
autoPlay,
drmConfig,
style,
}: BitmovinVideoPlayerType,
ref
) => {
const [loading, setLoading] = useState(false);
const [maxHeight, setMaxHeight] = useState<number | null>(null);
const [layout, setLayout] = useState<LayoutRectangle | null>(null);
const playerRef = useRef();
// this need because video view stretched on initial render (RN 0.55.4)
// TODO: check in future releases of RN
const _onReady = React.useCallback(
() => {
console.log("layout: ",layout," masHight: ", maxHeight)
if (Platform.OS === "android") {
if (layout && maxHeight === null) {
const { height } = layout;
setMaxHeight(height - 1);
}
}
},
[layout,maxHeight],
)
useEffect(() => {
const { height } = layout || {};
if ( maxHeight !== null && height && !loading) {
setTimeout(() => {
setMaxHeight(height);
}, 300);
console.log("loading: ", loading)
}
if (height && maxHeight === height) {
setLoading(true);
}
}, [maxHeight, layout, loading]);
useEffect(() => {
console.log("autoplay: " ,autoPlay)
console.log("loading: ", loading)
if (loading && autoPlay && Platform.OS === "android") {
play();
}
}, [loading, autoPlay]);
const play = () => {
if (Platform.OS === "android") {
BitmovinVideoPlayerModule.play(
findNodeHandle(playerRef?.current || null)
);
} else {
BitmovinVideoPlayerModule.play();
}
};
const pause = () => {
if (Platform.OS === "android") {
BitmovinVideoPlayerModule.pause(
findNodeHandle(playerRef?.current || null)
);
} else {
BitmovinVideoPlayerModule.pause();
}
};
const destroy = () => {
if (Platform.OS === "android") {
BitmovinVideoPlayerModule.destroy(
findNodeHandle(playerRef.current || null)
);
} else {
BitmovinVideoPlayerModule.destroy();
}
};
const mute = () => {
BitmovinVideoPlayerModule.mute(
findNodeHandle(playerRef.current || null)
);
};
const unmute = () => {
BitmovinVideoPlayerModule.unmute(
findNodeHandle(playerRef.current || null)
);
};
const enterFullscreen = () => {
BitmovinVideoPlayerModule.enterFullscreen(
findNodeHandle(playerRef.current || null)
);
};
const exitFullscreen = () => {
BitmovinVideoPlayerModule.exitFullscreen(
findNodeHandle(playerRef.current || null)
);
};
useImperativeHandle(ref, () => ({
play,
pause,
destroy,
mute,
unmute,
enterFullscreen,
exitFullscreen,
}));
const _onEnterFullscreen = (event: any) => {
enterFullscreen();
if (onEnterFullscreen) {
onEnterFullscreen(event);
}
};
return (
<View
// eslint-disable-next-line react-native/no-inline-styles
style={{ flex: 1 }}
onLayout={(event) => {
setLayout(event?.nativeEvent.layout);
}}>
<BitmovinVideoPlayer
onEnterFullscreen={_onEnterFullscreen}
autoPlay={autoPlay}
ref={playerRef as any}
configuration={{
...configuration,
}}
drmConfig={{
...drmConfig,
}}
style={[
maxHeight != null
? {
maxHeight,
}
: null,
style,
]}
onReady={_onReady}
/>
</View>
);
}
);
The Custom fullscreen handler I’ve got so far is :
public class CustomFullscreenHandler implements FullscreenHandler {
private final Activity _activity;
private final PlayerView _playerView;
private final View decorView;
private boolean _isFullscreen;
public CustomFullscreenHandler(Activity activity, PlayerView playerView) {
this._activity = activity;
this._playerView = playerView;
this.decorView = activity.getWindow().getDecorView();
}
private void handleFullscreen(boolean fullscreen) {
this._isFullscreen = fullscreen;
this.doLayoutChanges(fullscreen);
this.doSystemUiVisibility(fullscreen);
this.windowsOrientation(fullscreen);
}
private void doSystemUiVisibility(final boolean fullscreen) {
this.decorView.post(() -> {
int uiParams = FullscreenUtil.getSystemUiVisibilityFlags(fullscreen, true);
CustomFullscreenHandler.this.decorView.setSystemUiVisibility(uiParams);
});
}
private void windowsOrientation(boolean fullScreen) {
int fullScreen1 = this._activity.getWindowManager().getDefaultDisplay().getRotation();
if (fullScreen) {
if (fullScreen1 != 3) {
this._activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
this._activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
} else {
this._activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
private void doLayoutChanges(final boolean fullscreen) {
Looper mainLooper = Looper.getMainLooper();
boolean isAlreadyMainLooper = Looper.myLooper() == mainLooper;
UpdateLayoutRunnable updateLayoutRunnable = new UpdateLayoutRunnable((AppCompatActivity) this._activity, fullscreen);
if (isAlreadyMainLooper) {
updateLayoutRunnable.run();
} else {
Handler handler = new Handler(mainLooper);
handler.post(updateLayoutRunnable);
}
}
private class UpdateLayoutRunnable implements Runnable {
private final AppCompatActivity activity;
private final boolean fullscreen;
private UpdateLayoutRunnable(AppCompatActivity activity, boolean fullscreen) {
this.activity = activity;
this.fullscreen = fullscreen;
}
@Override
public void run() {
if (CustomFullscreenHandler.this._playerView.getParent() instanceof ViewGroup) {
ViewGroup parentView = (ViewGroup) CustomFullscreenHandler.this._playerView.getParent();
for (int i = 0; i < parentView.getChildCount(); i++) {
View child = parentView.getChildAt(i);
if (child != _playerView) {
child.setVisibility(fullscreen ? View.GONE : View.VISIBLE);
child.getVisibility();
}
}
}
}
}
public void onFullscreenRequested() {
handleFullscreen(true);
}
public void onFullscreenExitRequested() {
handleFullscreen(false);
}
public void onResume() {
if (_isFullscreen) {
doSystemUiVisibility(true);
}
}
public void onPause() {
}
public void onDestroy() {
}
public boolean isFullscreen() {
return this._isFullscreen;
}
}
The JSX component where the BitmovinVideoPLayer
is used:
const VideoComponent = () => {
const [videoContext, setVideoContext] = useVideoContext();
const [isLoaded, setLoaded] = useState(false);
const navigation = useNavigation();
const [orientation, setOrientation] = useState();
const [token, setToken] = useState();
const videoSource = useMemo(() => {
return {
uri: joinUrl(config.api.base_url, videoContext.root_urls.dash_url),
};
}, [videoContext.root_urls.dash_url]);
const drmType = DRMType.WIDEVINE;
const drmURL = joinUrl(config.api.base_url, "drm/acquire-widevine-license");
const loadVideoListScreen = useCallback(() => {
navigation.navigate("Home");
setVideoContext((prevState) => ({
...prevState,
uuid: "",
}));
return true;
}, [navigation, setVideoContext]);
return isLoaded ? (
<View style={styles.container}>
<BitmovinVideoPlayer
style={styles.backgroundVideo}
onEnterFullscreen={() => console.log("pressed fullscreen!!!!")}
autoPlay={true}
configuration={{
url: videoSource.uri,
title: "hello",
style: {
fullscreenIcon: true,
},
}}
/>
</View>
) : (
<SafeAreaView style={styles.viewHeight}>
<ActivityIndicator></ActivityIndicator>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
backgroundVideo: {
bottom: 0,
left: 0,
position: "absolute",
right: 0,
top: 0,
},
container:{
height:"30%",
width:"100%"
},
iconClose: {
alignItems: "flex-end",
},
linearGradient: {
height: "25%",
opacity: 0.75,
position: "absolute",
width: "100%",
},
});
export default VideoComponent;
And this component is wrapped into a screen view which is handled by a navigation stack:
import React from "react";
import { StyleSheet, View } from "react-native";
import VideoComponent from "../components/videoComponent";
import VideoDetail from "../components/videoDetails";
import VideoFlatList from "../components/videoFlatList";
import { CategoryUUIDProvider } from "../context/categoryContext";
import { DataProvider } from "../context/dataContext";
import { PageProvider } from "../context/pageContext";
function VideoScreen() {
const styles = StyleSheet.create({
container: {
alignItems: "flex-start",
flex: 1,
justifyContent: "flex-start",
},
});
return (
<CategoryUUIDProvider>
<PageProvider>
<DataProvider>
<View style={styles.container}>
<VideoComponent />
<VideoDetail />
<VideoFlatList />
</View>
</DataProvider>
</PageProvider>
</CategoryUUIDProvider>
);
}
export default VideoScreen;
And here I attach a gif of the issue: