From 279cc0e5ed712488fc3c153c62b14f13048103f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Moska=C5=82a?= <91079590+moskalakamil@users.noreply.github.com> Date: Sun, 29 Sep 2024 20:51:02 +0200 Subject: [PATCH] feat(android): allow to hide specific controls (#4183) * feat(android): enable to hide specific controls * fix: ts * fix: lint * docs: update `controlsStyles` docs --- .../brentvatne/common/api/ControlsConfig.kt | 20 +++- .../exoplayer/ReactExoplayerView.java | 113 +++++++++++++----- docs/pages/component/props.mdx | 26 +++- src/specs/VideoNativeComponent.ts | 9 +- src/types/video.ts | 9 +- 5 files changed, 135 insertions(+), 42 deletions(-) diff --git a/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt b/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt index a50482c658..ed231076d1 100644 --- a/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt +++ b/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt @@ -5,11 +5,20 @@ import com.facebook.react.bridge.ReadableMap class ControlsConfig { var hideSeekBar: Boolean = false - var seekIncrementMS: Int = 10000 var hideDuration: Boolean = false + + var hidePosition: Boolean = false + var hidePlayPause: Boolean = false + var hideForward: Boolean = false + var hideRewind: Boolean = false + var hideNext: Boolean = false + var hidePrevious: Boolean = false + var hideFullscreen: Boolean = false var hideNavigationBarOnFullScreenMode: Boolean = true var hideNotificationBarOnFullScreenMode: Boolean = true + var seekIncrementMS: Int = 10000 + companion object { @JvmStatic fun parse(src: ReadableMap?): ControlsConfig { @@ -17,8 +26,15 @@ class ControlsConfig { if (src != null) { config.hideSeekBar = ReactBridgeUtils.safeGetBool(src, "hideSeekBar", false) - config.seekIncrementMS = ReactBridgeUtils.safeGetInt(src, "seekIncrementMS", 10000) config.hideDuration = ReactBridgeUtils.safeGetBool(src, "hideDuration", false) + config.hidePosition = ReactBridgeUtils.safeGetBool(src, "hidePosition", false) + config.hidePlayPause = ReactBridgeUtils.safeGetBool(src, "hidePlayPause", false) + config.hideForward = ReactBridgeUtils.safeGetBool(src, "hideForward", false) + config.hideRewind = ReactBridgeUtils.safeGetBool(src, "hideRewind", false) + config.hideNext = ReactBridgeUtils.safeGetBool(src, "hideNext", false) + config.hidePrevious = ReactBridgeUtils.safeGetBool(src, "hidePrevious", false) + config.hideFullscreen = ReactBridgeUtils.safeGetBool(src, "hideFullscreen", false) + config.seekIncrementMS = ReactBridgeUtils.safeGetInt(src, "seekIncrementMS", 10000) config.hideNavigationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(src, "hideNavigationBarOnFullScreenMode", true) config.hideNotificationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(src, "hideNotificationBarOnFullScreenMode", true) } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 0714115e50..46ec5ed252 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -438,6 +438,7 @@ public void onVisibilityChange(int visibility) { //Handling the playButton click event ImageButton playButton = playerControlView.findViewById(R.id.exo_play); + playButton.setOnClickListener((View v) -> { if (player != null && player.getPlaybackState() == Player.STATE_ENDED) { player.seekTo(0); @@ -466,7 +467,7 @@ public void onVisibilityChange(int visibility) { final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen); fullScreenButton.setOnClickListener(v -> setFullscreen(!isFullscreen)); updateFullScreenButtonVisibility(); - refreshProgressBarVisibility(); + refreshControlsStyles(); // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player eventListener = new Player.Listener() { @@ -480,6 +481,7 @@ public void onPlaybackStateChanged(int playbackState) { if (pauseButton != null && pauseButton.getVisibility() == GONE) { pauseButton.setVisibility(INVISIBLE); } + reLayout(playPauseControlContainer); //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done player.removeListener(eventListener); @@ -525,38 +527,85 @@ private void reLayout(View view) { view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight()); } - private void refreshProgressBarVisibility (){ + private void refreshControlsStyles (){ if(playerControlView == null) return; - DefaultTimeBar exoProgress; - TextView exoDuration; - TextView exoPosition; - exoProgress = playerControlView.findViewById(R.id.exo_progress); - exoDuration = playerControlView.findViewById(R.id.exo_duration); - exoPosition = playerControlView.findViewById(R.id.exo_position); - if(controlsConfig.getHideSeekBar()){ - LinearLayout.LayoutParams param = new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT, - 1.0f - ); - exoProgress.setVisibility(GONE); - exoDuration.setVisibility(GONE); - exoPosition.setLayoutParams(param); - }else{ - exoProgress.setVisibility(VISIBLE); - - if(controlsConfig.getHideDuration()){ - exoDuration.setVisibility(GONE); - }else{ - exoDuration.setVisibility(VISIBLE); - } - // Reset the layout parameters of exoPosition to their default state - LinearLayout.LayoutParams defaultParam = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT - ); - exoPosition.setLayoutParams(defaultParam); + final ImageButton playButton = playerControlView.findViewById(R.id.exo_play); + final ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause); + if (controlsConfig.getHidePlayPause()) { + playPauseControlContainer.setAlpha(0); + + playButton.setClickable(false); + pauseButton.setClickable(false); + } else { + playPauseControlContainer.setAlpha(1.0f); + + playButton.setClickable(true); + pauseButton.setClickable(true); + } + + final ImageButton forwardButton = playerControlView.findViewById(R.id.exo_ffwd); + if (controlsConfig.getHideForward()) { + forwardButton.setImageAlpha(0); + forwardButton.setClickable(false); + } else { + forwardButton.setImageAlpha(255); + forwardButton.setClickable(true); + } + + final ImageButton rewindButton = playerControlView.findViewById(R.id.exo_rew); + if (controlsConfig.getHideRewind()) { + rewindButton.setImageAlpha(0); + rewindButton.setClickable(false); + } else { + rewindButton.setImageAlpha(255); + rewindButton.setClickable(true); + } + + final ImageButton nextButton = playerControlView.findViewById(R.id.exo_next); + if (controlsConfig.getHideNext()) { + nextButton.setClickable(false); + nextButton.setImageAlpha(0); + } else { + nextButton.setImageAlpha(255); + nextButton.setClickable(true); + } + + final ImageButton previousButton = playerControlView.findViewById(R.id.exo_prev); + if (controlsConfig.getHidePrevious()) { + previousButton.setImageAlpha(0); + previousButton.setClickable(false); + } else { + previousButton.setImageAlpha(255); + previousButton.setClickable(true); + } + + final ImageButton fullscreenButton = playerControlView.findViewById(R.id.exo_fullscreen); + if (controlsConfig.getHideFullscreen()) { + fullscreenButton.setVisibility(GONE); + } else if (fullscreenButton.getVisibility() == GONE) { + fullscreenButton.setVisibility(VISIBLE); + } + + final TextView positionText = playerControlView.findViewById(R.id.exo_position); + if(controlsConfig.getHidePosition()){ + positionText.setVisibility(GONE); + } else if (positionText.getVisibility() == GONE){ + positionText.setVisibility(VISIBLE); + } + + final DefaultTimeBar progressBar = playerControlView.findViewById(R.id.exo_progress); + if (controlsConfig.getHideSeekBar()) { + progressBar.setVisibility(INVISIBLE); + } else if (progressBar.getVisibility() == INVISIBLE) { + progressBar.setVisibility(VISIBLE); + } + + final TextView durationText = playerControlView.findViewById(R.id.exo_duration); + if (controlsConfig.getHideDuration()) { + durationText.setVisibility(GONE); + } else if (durationText.getVisibility() == GONE) { + durationText.setVisibility(VISIBLE); } } @@ -2389,6 +2438,6 @@ public void onAdError(AdErrorEvent adErrorEvent) { public void setControlsStyles(ControlsConfig controlsStyles) { controlsConfig = controlsStyles; - refreshProgressBarVisibility(); + refreshControlsStyles(); } } \ No newline at end of file diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index f976dfbc99..f7ee531af2 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -146,21 +146,35 @@ Adjust the control styles. This prop is need only if `controls={true}` and is an | Property | Type | Description | |-----------------------------------|---------|--------------------------------------------------------------------------------------------| -| hideSeekBar | boolean | The default value is `false`, allowing you to hide the seek bar for live broadcasts. | -| hideDuration | boolean | The default value is `false`, allowing you to hide the duration. | -| seekIncrementMS | number | The default value is `10000`. You can change the value to increment forward and rewind. | -| hideNavigationBarOnFullScreenMode | boolean | The default value is `true`, allowing you to hide the navigation bar on full-screen mode. | +| hidePosition | boolean | Hides the position indicator. Default is `false`. | +| hidePlayPause | boolean | Hides the play/pause button. Default is `false`. | +| hideForward | boolean | Hides the forward button. Default is `false`. | +| hideRewind | boolean | Hides the rewind button. Default is `false`. | +| hideNext | boolean | Hides the next button. Default is `false`. | +| hidePrevious | boolean | Hides the previous button. Default is `false`. | +| hideFullscreen | boolean | Hides the fullscreen button. Default is `false`. | +| hideSeekBar | boolean | The default value is `false`, allowing you to hide the seek bar for live broadcasts. | +| hideDuration | boolean | The default value is `false`, allowing you to hide the duration. | +| hideNavigationBarOnFullScreenMode | boolean | The default value is `true`, allowing you to hide the navigation bar on full-screen mode. | | hideNotificationBarOnFullScreenMode | boolean | The default value is `true`, allowing you to hide the notification bar on full-screen mode. | +| seekIncrementMS | number | The default value is `10000`. You can change the value to increment forward and rewind. | Example with default values: ```javascript controlsStyles={{ + hidePosition: false, + hidePlayPause: false, + hideForward: false, + hideRewind: false, + hideNext: false, + hidePrevious: false, + hideFullscreen: false, hideSeekBar: false, hideDuration: false, - seekIncrementMS: 10000, hideNavigationBarOnFullScreenMode: true, hideNotificationBarOnFullScreenMode: true, + seekIncrementMS: 10000, }} ``` @@ -510,7 +524,7 @@ If `renderLoader` is provided, `poster` and `posterResizeMode` will be ignored. renderLoader is either a component or a function returning a component. It is recommended to use the function for optimization matter. -`renderLoader` function be called with parameters of type `ReactVideoRenderLoaderProps` to be able to adapt loader +`renderLoader` function be called with parameters of type `ReactVideoRenderLoaderProps` to be able to adapt loader ```typescript interface ReactVideoRenderLoaderProps { diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 34a50438ad..165c4949ab 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -297,11 +297,18 @@ export type OnAudioFocusChangedData = Readonly<{ }>; type ControlsStyles = Readonly<{ + hidePosition?: WithDefault; + hidePlayPause?: WithDefault; + hideForward?: WithDefault; + hideRewind?: WithDefault; + hideNext?: WithDefault; + hidePrevious?: WithDefault; + hideFullscreen?: WithDefault; hideSeekBar?: WithDefault; hideDuration?: WithDefault; - seekIncrementMS?: Int32; hideNavigationBarOnFullScreenMode?: WithDefault; hideNotificationBarOnFullScreenMode?: WithDefault; + seekIncrementMS?: Int32; }>; export type OnControlsVisibilityChange = Readonly<{ diff --git a/src/types/video.ts b/src/types/video.ts index 9e25e25ea5..e9fe31913a 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -251,9 +251,16 @@ export type AudioOutput = 'speaker' | 'earpiece'; export type ControlsStyles = { hideSeekBar?: boolean; hideDuration?: boolean; - seekIncrementMS?: number; + hidePosition?: boolean; + hidePlayPause?: boolean; + hideForward?: boolean; + hideRewind?: boolean; + hideNext?: boolean; + hidePrevious?: boolean; + hideFullscreen?: boolean; hideNavigationBarOnFullScreenMode?: boolean; hideNotificationBarOnFullScreenMode?: boolean; + seekIncrementMS?: number; }; export interface ReactVideoRenderLoaderProps {