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