diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 7b870556569..940f18ec18c 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -187,6 +187,8 @@ public final class VideoDetailFragment
} else if (getString(R.string.show_description_key).equals(key)) {
showDescription = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true;
+ } else if (getString(R.string.pinned_video_player_key).equals(key)) {
+ setupPinnedPlayerMode();
}
};
@@ -207,6 +209,9 @@ public final class VideoDetailFragment
@State
protected boolean autoPlayEnabled = true;
+ // Pinned player fields
+ private boolean isPinnedPlayerEnabled = false;
+
@Nullable
private StreamInfo currentInfo = null;
private Disposable currentWorker;
@@ -627,6 +632,9 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
binding.detailThumbnailRootLayout.requestFocus();
+ // Setup pinned player mode
+ setupPinnedPlayerMode();
+
binding.detailControlsPlayWithKodi.setVisibility(
KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId)
? View.VISIBLE
@@ -1271,7 +1279,14 @@ private void tryAddVideoPlayerView() {
if (binding != null) {
// prevent from re-adding a view multiple times
playerUi.removeViewFromParent();
- binding.playerPlaceholder.addView(playerUi.getBinding().getRoot());
+
+ // Add to appropriate placeholder based on pinned mode
+ if (isPinnedPlayerEnabled) {
+ binding.pinnedPlayerPlaceholder.addView(playerUi.getBinding().getRoot());
+ } else {
+ binding.playerPlaceholder.addView(playerUi.getBinding().getRoot());
+ }
+
playerUi.setupVideoSurfaceIfNeeded();
}
});
@@ -1295,6 +1310,85 @@ private void makeDefaultHeightForVideoPlaceholder() {
binding.playerPlaceholder.requestLayout();
}
+ /*//////////////////////////////////////////////////////////////////////////
+ // Pinned Player Methods
+ //////////////////////////////////////////////////////////////////////////*/
+
+ /**
+ * Check if pinned player mode is enabled in settings.
+ *
+ * @return true if pinned player mode is enabled, false otherwise
+ */
+ private boolean isPinnedPlayerEnabled() {
+ return PreferenceManager.getDefaultSharedPreferences(requireContext())
+ .getBoolean(getString(R.string.pinned_video_player_key), false);
+ }
+
+ /**
+ * Setup pinned player mode based on user preference.
+ */
+ private void setupPinnedPlayerMode() {
+ isPinnedPlayerEnabled = isPinnedPlayerEnabled();
+
+ if (binding == null) {
+ return;
+ }
+
+ if (isPinnedPlayerEnabled) {
+ // Show pinned overlay, hide original content
+ binding.pinnedPlayerOverlay.setVisibility(View.VISIBLE);
+ binding.detailMainContent.setVisibility(View.GONE);
+
+ // Copy data to pinned layout
+ copyDataToPinnedLayout();
+
+ // Setup tabs for pinned mode
+ setupPinnedTabs();
+ } else {
+ // Show original layout, hide pinned overlay
+ binding.pinnedPlayerOverlay.setVisibility(View.GONE);
+ binding.detailMainContent.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Copy video data to pinned layout elements.
+ */
+ private void copyDataToPinnedLayout() {
+ if (currentInfo == null || binding == null) {
+ return;
+ }
+
+ // Copy title
+ binding.pinnedDetailTitleView.setText(currentInfo.getName());
+
+ // Copy uploader info
+ binding.pinnedDetailUploaderNameView.setText(currentInfo.getUploaderName());
+ // Note: Subscriber count and description methods may not be available
+ // binding.pinnedDetailUploaderSubscriberCountView.setText(
+ // currentInfo.getSubscriberCount());
+ // binding.pinnedDetailDescriptionView.setText(currentInfo.getDescription());
+
+ // Load uploader thumbnail - check if method exists
+ // if (currentInfo.getUploaderAvatarUrl() != null) {
+ // PicassoHelper.loadAvatarImage(currentInfo.getUploaderAvatarUrl())
+ // .into(binding.pinnedDetailUploaderThumbnailView);
+ // }
+ }
+
+ /**
+ * Setup tabs for pinned mode (similar to original tabs).
+ */
+ private void setupPinnedTabs() {
+ if (binding == null || pageAdapter == null) {
+ return;
+ }
+
+ // Setup ViewPager and TabLayout for pinned mode
+ binding.pinnedViewPager.setAdapter(pageAdapter);
+ binding.pinnedTabLayout.setupWithViewPager(binding.pinnedViewPager);
+ }
+
private final ViewTreeObserver.OnPreDrawListener preDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -1499,6 +1593,9 @@ public void handleResult(@NonNull final StreamInfo info) {
updateTabs(info);
+ // Update pinned player data when video info is loaded
+ copyDataToPinnedLayout();
+
animate(binding.detailThumbnailPlayButton, true, 200);
binding.detailVideoTitleView.setText(title);
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index 1a4711581e2..ec7b65d21b7 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -704,4 +704,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-v35/styles.xml b/app/src/main/res/values-v35/styles.xml
deleted file mode 100644
index beb16bcdfbb..00000000000
--- a/app/src/main/res/values-v35/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 147c88938a9..49ee6d4cdd2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -91,6 +91,9 @@
The active player queue will be replaced
Ignore hardware media button events
Useful, for instance, if you are using a headset with broken physical buttons
+ pinned_video_player_key
+ Pin video player at top
+ Keep the video player visible at the top while scrolling through comments and recommendations
Show comments
Turn off to hide comments
Show \'Next\' and \'Similar\' videos
diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml
index 727ce4df40a..da2fd9049b6 100644
--- a/app/src/main/res/xml/video_audio_settings.xml
+++ b/app/src/main/res/xml/video_audio_settings.xml
@@ -249,5 +249,13 @@
android:title="@string/ignore_hardware_media_buttons_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
+
+