diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java index 0f163b25..be6166b4 100644 --- a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java +++ b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java @@ -37,6 +37,7 @@ public class VideoEventEmitter { private static final String EVENT_ERROR = "onVideoError"; private static final String EVENT_PROGRESS = "onVideoProgress"; private static final String EVENT_BANDWIDTH = "onVideoBandwidthUpdate"; + private static final String EVENT_CONTROLS_VISIBILITY_CHANGE = "onControlsVisibilityChange"; private static final String EVENT_SEEK = "onVideoSeek"; private static final String EVENT_END = "onVideoEnd"; private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent"; @@ -89,6 +90,7 @@ public class VideoEventEmitter { EVENT_TEXT_TRACK_DATA_CHANGED, EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, + EVENT_CONTROLS_VISIBILITY_CHANGE, EVENT_ON_RECEIVE_AD_EVENT }; @@ -120,6 +122,7 @@ public class VideoEventEmitter { EVENT_TEXT_TRACK_DATA_CHANGED, EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, + EVENT_CONTROLS_VISIBILITY_CHANGE, EVENT_ON_RECEIVE_AD_EVENT }) @interface VideoEvents { @@ -164,6 +167,8 @@ public class VideoEventEmitter { private static final String EVENT_PROP_IS_PLAYING = "isPlaying"; + private static final String EVENT_CONTROLS_VISIBLE = "isVisible"; + public void setViewId(int viewId) { this.viewId = viewId; } @@ -354,6 +359,12 @@ public class VideoEventEmitter { receiveEvent(EVENT_END, null); } + public void controlsVisibilityChanged(boolean isVisible) { + WritableMap map = Arguments.createMap(); + map.putBoolean(EVENT_CONTROLS_VISIBLE, isVisible); + receiveEvent(EVENT_CONTROLS_VISIBILITY_CHANGE, map); + } + public void fullscreenWillPresent() { receiveEvent(EVENT_FULLSCREEN_WILL_PRESENT, null); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 27a3478e..e0a14a71 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -416,6 +416,12 @@ public class ReactExoplayerView extends FrameLayout implements private void initializePlayerControl() { if (playerControlView == null) { playerControlView = new LegacyPlayerControlView(getContext()); + playerControlView.addVisibilityListener(new LegacyPlayerControlView.VisibilityListener() { + @Override + public void onVisibilityChange(int visibility) { + eventEmitter.controlsVisibilityChanged(visibility == View.VISIBLE); + } + }); } if (fullScreenPlayerView == null) { diff --git a/docs/pages/component/events.mdx b/docs/pages/component/events.mdx index 71354757..146c0d65 100644 --- a/docs/pages/component/events.mdx +++ b/docs/pages/component/events.mdx @@ -121,6 +121,26 @@ Example: } ``` +### `onControlsVisibilityChange` + + + +Callback function that is called when the controls are hidden or shown. Not possible on iOS. + +Payload: + +| Property | Type | Description | +| ----------- | ------- | ---------------------------------------------- | +| isVisible | boolean | Boolean indicating whether controls are visible | + +Example: + +```javascript +{ + isVisible: true; +} +``` + ### `onEnd` diff --git a/src/Video.tsx b/src/Video.tsx index 627c9930..fbad49c5 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -22,6 +22,7 @@ import NativeVideoComponent, { type OnAudioTracksData, type OnBandwidthUpdateData, type OnBufferData, + type OnControlsVisibilityChange, type OnExternalPlaybackChangeData, type OnGetLicenseData, type OnLoadStartData, @@ -91,6 +92,7 @@ const Video = forwardRef( onEnd, onBuffer, onBandwidthUpdate, + onControlsVisibilityChange, onExternalPlaybackChange, onFullscreenPlayerWillPresent, onFullscreenPlayerDidPresent, @@ -482,6 +484,13 @@ const Video = forwardRef( [onAspectRatio], ); + const _onControlsVisibilityChange = useCallback( + (e: NativeSyntheticEvent) => { + onControlsVisibilityChange?.(e.nativeEvent); + }, + [onControlsVisibilityChange], + ); + const useExternalGetLicense = drm?.getLicense instanceof Function; const onGetLicense = useCallback( @@ -639,6 +648,9 @@ const Video = forwardRef( ? (_onReceiveAdEvent as (e: NativeSyntheticEvent) => void) : undefined } + onControlsVisibilityChange={ + onControlsVisibilityChange ? _onControlsVisibilityChange : undefined + } /> {hasPoster && showPoster ? ( diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index f6e8ee6f..a64dbb73 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -289,6 +289,10 @@ type ControlsStyles = Readonly<{ seekIncrementMS?: number; }>; +export type OnControlsVisibilityChange = Readonly<{ + isVisible: boolean; +}>; + export interface VideoNativeProps extends ViewProps { src?: VideoSrc; drm?: Drm; @@ -337,6 +341,7 @@ export interface VideoNativeProps extends ViewProps { useSecureView?: boolean; // Android bufferingStrategy?: BufferingStrategyType; // Android controlsStyles?: ControlsStyles; // Android + onControlsVisibilityChange?: DirectEventHandler; onVideoLoad?: DirectEventHandler; onVideoLoadStart?: DirectEventHandler; onVideoAspectRatio?: DirectEventHandler; diff --git a/src/types/events.ts b/src/types/events.ts index c48e769f..6d3e0d9e 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -4,6 +4,7 @@ import type { OnAudioTracksData, OnBandwidthUpdateData, OnBufferData, + OnControlsVisibilityChange, OnExternalPlaybackChangeData, OnLoadStartData, OnPictureInPictureStatusChangedData, @@ -237,6 +238,7 @@ export interface ReactVideoEvents { onIdle?: () => void; // Android onBandwidthUpdate?: (e: OnBandwidthUpdateData) => void; //Android onBuffer?: (e: OnBufferData) => void; //Android, iOS + onControlsVisibilityChange?: (e: OnControlsVisibilityChange) => void; // Android, iOS onEnd?: () => void; //All onError?: (e: OnVideoErrorData) => void; //Android, iOS onExternalPlaybackChange?: (e: OnExternalPlaybackChangeData) => void; //iOS