From d1883a7e008706cac2a2beac934194539c9b5b77 Mon Sep 17 00:00:00 2001 From: Seyed Mostafa Hasani Date: Sat, 5 Oct 2024 20:04:25 +0330 Subject: [PATCH] feat(android): add settings button to control video playback speed (#4211) --- .../brentvatne/common/api/ControlsConfig.kt | 3 +- .../exoplayer/ReactExoplayerView.java | 37 ++++++++++++++++++- .../layout/exo_legacy_player_control_view.xml | 7 ++++ android/src/main/res/values/strings.xml | 6 +++ docs/pages/component/props.mdx | 2 + src/specs/VideoNativeComponent.ts | 1 + src/types/video.ts | 1 + 7 files changed, 55 insertions(+), 2 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 a09478c7..8ea70014 100644 --- a/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt +++ b/android/src/main/java/com/brentvatne/common/api/ControlsConfig.kt @@ -6,7 +6,6 @@ import com.facebook.react.bridge.ReadableMap class ControlsConfig { var hideSeekBar: Boolean = false var hideDuration: Boolean = false - var hidePosition: Boolean = false var hidePlayPause: Boolean = false var hideForward: Boolean = false @@ -17,6 +16,7 @@ class ControlsConfig { var hideNavigationBarOnFullScreenMode: Boolean = true var hideNotificationBarOnFullScreenMode: Boolean = true var liveLabel: String? = null + var hideSettingButton: Boolean = true var seekIncrementMS: Int = 10000 @@ -39,6 +39,7 @@ class ControlsConfig { config.hideNavigationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(controlsConfig, "hideNavigationBarOnFullScreenMode", true) config.hideNotificationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(controlsConfig, "hideNotificationBarOnFullScreenMode", true) config.liveLabel = ReactBridgeUtils.safeGetString(controlsConfig, "liveLabel", null) + config.hideSettingButton = ReactBridgeUtils.safeGetBool(controlsConfig, "hideSettingButton", true) } return config } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index bb141b3b..aec2d692 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -10,6 +10,7 @@ import static androidx.media3.common.C.TIME_END_OF_SOURCE; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; +import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -99,7 +100,6 @@ import androidx.media3.extractor.metadata.emsg.EventMessage; import androidx.media3.extractor.metadata.id3.Id3Frame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.session.MediaSessionService; -import androidx.media3.ui.DefaultTimeBar; import androidx.media3.ui.LegacyPlayerControlView; import com.brentvatne.common.api.BufferConfig; @@ -258,6 +258,7 @@ public class ReactExoplayerView extends FrameLayout implements private long lastDuration = -1; private boolean viewHasDropped = false; + private int selectedSpeedIndex = 1; // Default is 1.0x private final String instanceId = String.valueOf(UUID.randomUUID()); @@ -463,6 +464,10 @@ public class ReactExoplayerView extends FrameLayout implements setPausedModifier(true) ); + //Handling the settingButton click event + final ImageButton settingButton = playerControlView.findViewById(R.id.exo_settings); + settingButton.setOnClickListener(v -> openSettings()); + //Handling the fullScreenButton click event final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen); fullScreenButton.setOnClickListener(v -> setFullscreen(!isFullscreen)); @@ -496,6 +501,35 @@ public class ReactExoplayerView extends FrameLayout implements }; player.addListener(eventListener); } + private void openSettings() { + AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext); + builder.setTitle(R.string.settings); + String[] settingsOptions = {themedReactContext.getString(R.string.playback_speed)}; + builder.setItems(settingsOptions, (dialog, which) -> { + if (which == 0) { + showPlaybackSpeedOptions(); + } + }); + builder.show(); + } + + private void showPlaybackSpeedOptions() { + String[] speedOptions = {"0.5x", "1.0x", "1.5x", "2.0x"}; + AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext); + builder.setTitle(R.string.select_playback_speed); + + builder.setSingleChoiceItems(speedOptions, selectedSpeedIndex, (dialog, which) -> { + selectedSpeedIndex = which; + float speed = switch (which) { + case 0 -> 0.5f; + case 2 -> 1.5f; + case 3 -> 2.0f; + default -> 1.0f; + }; + setRateModifier(speed); + }); + builder.show(); + } /** * Adding Player control to the frame layout @@ -539,6 +573,7 @@ public class ReactExoplayerView extends FrameLayout implements updateViewVisibility(playerControlView.findViewById(R.id.exo_position), controlsConfig.getHidePosition(), GONE); updateViewVisibility(playerControlView.findViewById(R.id.exo_progress), controlsConfig.getHideSeekBar(), INVISIBLE); updateViewVisibility(playerControlView.findViewById(R.id.exo_duration), controlsConfig.getHideDuration(), GONE); + updateViewVisibility(playerControlView.findViewById(R.id.exo_settings), controlsConfig.getHideSettingButton(), GONE ); } private void updateLiveContent() { diff --git a/android/src/main/res/layout/exo_legacy_player_control_view.xml b/android/src/main/res/layout/exo_legacy_player_control_view.xml index 0d4800a8..61b84733 100644 --- a/android/src/main/res/layout/exo_legacy_player_control_view.xml +++ b/android/src/main/res/layout/exo_legacy_player_control_view.xml @@ -101,6 +101,13 @@ android:includeFontPadding="false" android:textColor="@color/silver_gray"/> + + This device does not support the required DRM scheme An unknown DRM error occurred + + Settings + + Playback Speed + + Select Playback Speed diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index 8395a300..d636dd63 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -157,6 +157,7 @@ Adjust the control styles. This prop is need only if `controls={true}` and is an | 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. | +| hideSettingButton | boolean | The default value is `true`, allowing you to hide the setting button. | | seekIncrementMS | number | The default value is `10000`. You can change the value to increment forward and rewind. | | liveLabel | string | Allowing you to set a label for live video. | @@ -175,6 +176,7 @@ controlsStyles={{ hideDuration: false, hideNavigationBarOnFullScreenMode: true, hideNotificationBarOnFullScreenMode: true, + hideSettingButton: true, seekIncrementMS: 10000, liveLabel: "LIVE" }} diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index e9e69dfa..800f05d2 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -308,6 +308,7 @@ type ControlsStyles = Readonly<{ hideDuration?: WithDefault; hideNavigationBarOnFullScreenMode?: WithDefault; hideNotificationBarOnFullScreenMode?: WithDefault; + hideSettingButton?: WithDefault; seekIncrementMS?: Int32; liveLabel?: string; }>; diff --git a/src/types/video.ts b/src/types/video.ts index ab71d69f..1d6e9077 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -260,6 +260,7 @@ export type ControlsStyles = { hideFullscreen?: boolean; hideNavigationBarOnFullScreenMode?: boolean; hideNotificationBarOnFullScreenMode?: boolean; + hideSettingButton?: boolean; seekIncrementMS?: number; liveLabel?: string; };