From 150e156d26162063304dcf8ba361598f749cb697 Mon Sep 17 00:00:00 2001
From: Avently <7953703+avently@users.noreply.github.com>
Date: Tue, 15 Sep 2020 14:43:43 +0300
Subject: [PATCH] Reimagined player positioning
---
.../fragments/detail/VideoDetailFragment.java | 27 +++-
.../newpipe/player/VideoPlayerImpl.java | 143 ++++--------------
.../org/schabi/newpipe/util/DeviceUtils.java | 15 --
.../views/CustomCollapsingToolbarLayout.java | 36 +++++
.../fragment_video_detail.xml | 4 +-
app/src/main/res/layout-large-land/player.xml | 1 +
.../main/res/layout/fragment_video_detail.xml | 5 +-
app/src/main/res/layout/player.xml | 1 +
8 files changed, 90 insertions(+), 142 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java
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 eddefaa8c..6ab9d8995 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
@@ -11,11 +11,13 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.database.ContentObserver;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import androidx.core.app.ActivityCompat;
import androidx.core.text.HtmlCompat;
import androidx.preference.PreferenceManager;
import android.provider.Settings;
@@ -2002,8 +2004,8 @@ public class VideoDetailFragment
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
}
activity.getWindow().getDecorView().setSystemUiVisibility(0);
- activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
- if (Build.VERSION.SDK_INT >= 30 /*Android 11*/) {
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(ThemeHelper.resolveColorFromAttr(
requireContext(), android.R.attr.colorPrimary));
}
@@ -2021,18 +2023,27 @@ public class VideoDetailFragment
// Prevent jumping of the player on devices with cutout
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
- final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ // In multiWindow mode status bar is not transparent for devices with cutout
+ // if I include this flag. So without it is better in this case
+ if (!isInMultiWindow()) {
+ visibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+ }
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
- activity.getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
- WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
+ && (isInMultiWindow() || (player != null && player.isFullscreen()))) {
+ activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
+ activity.getWindow().setNavigationBarColor(
+ ActivityCompat.getColor(activity, R.color.video_overlay_color));
+ }
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
// Listener implementation
diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
index ede476653..40d4a9cd4 100644
--- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
@@ -27,23 +27,22 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
+import android.view.DisplayCutout;
+import androidx.annotation.ColorInt;
+import androidx.core.app.ActivityCompat;
import androidx.preference.PreferenceManager;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
-import android.view.Display;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -103,7 +102,6 @@ import org.schabi.newpipe.util.ShareUtils;
import java.util.List;
import static android.content.Context.WINDOW_SERVICE;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
@@ -339,7 +337,6 @@ public class VideoPlayerImpl extends VideoPlayer
* This method ensures that popup and main players have different look.
* We use one layout for both players and need to decide what to show and what to hide.
* Additional measuring should be done inside {@link #setupElementsSize}.
- * {@link #setControlsSize} is used to adapt the UI to fullscreen mode, multiWindow, navBar, etc
*/
private void setupElementsVisibility() {
if (popupPlayerSelected()) {
@@ -474,6 +471,17 @@ public class VideoPlayerImpl extends VideoPlayer
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
settingsContentObserver);
getRootView().addOnLayoutChangeListener(this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ queueLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> {
+ final DisplayCutout cutout = windowInsets.getDisplayCutout();
+ if (cutout != null) {
+ view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+ cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
+ }
+ return windowInsets;
+ });
+ }
}
public boolean onKeyDown(final int keyCode) {
@@ -746,7 +754,8 @@ public class VideoPlayerImpl extends VideoPlayer
}
isFullscreen = !isFullscreen;
- setControlsSize();
+ // Prevent applying windows insets twice (open vertical video to reproduce)
+ getRootView().findViewById(R.id.playbackControlRoot).setPadding(0, 0, 0, 0);
fragmentListener.onFullscreenStateChanged(isFullscreen());
}
@@ -1264,20 +1273,6 @@ public class VideoPlayerImpl extends VideoPlayer
updatePopupSize(getPopupLayoutParams().width, -1);
checkPopupPositionBounds();
}
-
- // The only situation I need to re-calculate elements sizes is
- // when a user rotates a device from landscape to landscape
- // because in that case the controls should be aligned to another side of a screen.
- // The problem is when user leaves the app and returns back
- // (while the app in landscape) Android reports via DisplayMetrics that orientation
- // is portrait and it gives wrong sizes calculations.
- // Let's skip re-calculation in every case but landscape
- final boolean reportedOrientationIsLandscape = service.isLandscape();
- final boolean actualOrientationIsLandscape = context.getResources()
- .getConfiguration().orientation == ORIENTATION_LANDSCAPE;
- if (reportedOrientationIsLandscape && actualOrientationIsLandscape) {
- setControlsSize();
- }
// Close it because when changing orientation from portrait
// (in fullscreen mode) the size of queue layout can be larger than the screen size
onQueueClosed();
@@ -1471,14 +1466,19 @@ public class VideoPlayerImpl extends VideoPlayer
}
private void showSystemUIPartially() {
- if (isFullscreen() && getParentActivity() != null) {
+ final AppCompatActivity activity = getParentActivity();
+ if (isFullscreen() && activity != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ @ColorInt final int systemUiColor =
+ ActivityCompat.getColor(service, R.color.video_overlay_color);
+ activity.getWindow().setStatusBarColor(systemUiColor);
+ activity.getWindow().setNavigationBarColor(systemUiColor);
+ }
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
- if (Build.VERSION.SDK_INT >= 30 /*Android 11*/) {
- getParentActivity().getWindow().setStatusBarColor(Color.TRANSPARENT);
- }
+ activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
+ activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
@@ -1489,92 +1489,6 @@ public class VideoPlayerImpl extends VideoPlayer
}
}
- /**
- * Measures width and height of controls visible on screen.
- * It ensures that controls will be side-by-side with NavigationBar and notches
- * but not under them. Tablets have only bottom NavigationBar
- */
- public void setControlsSize() {
- final Point size = new Point();
- final Display display = getRootView().getDisplay();
- if (display == null || !videoPlayerSelected()) {
- return;
- }
- // This method will give a correct size of a usable area of a window.
- // It doesn't include NavigationBar, notches, etc.
- display.getSize(size);
-
- final boolean isLandscape = service.isLandscape();
- final int width = isFullscreen
- ? (isLandscape ? size.x : size.y)
- : ViewGroup.LayoutParams.MATCH_PARENT;
- final int gravity = isFullscreen
- ? (display.getRotation() == Surface.ROTATION_90
- ? Gravity.START : Gravity.END)
- : Gravity.TOP;
-
- getTopControlsRoot().getLayoutParams().width = width;
- final RelativeLayout.LayoutParams topParams =
- ((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
- topParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
- topParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
- topParams.addRule(gravity == Gravity.END
- ? RelativeLayout.ALIGN_PARENT_END
- : RelativeLayout.ALIGN_PARENT_START);
- getTopControlsRoot().requestLayout();
-
- getBottomControlsRoot().getLayoutParams().width = width;
- final RelativeLayout.LayoutParams bottomParams =
- ((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
- bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
- bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
- bottomParams.addRule(gravity == Gravity.END
- ? RelativeLayout.ALIGN_PARENT_END
- : RelativeLayout.ALIGN_PARENT_START);
- getBottomControlsRoot().requestLayout();
-
- final ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
- // In tablet navigationBar located at the bottom of the screen.
- // And the situations when we need to set custom height is
- // in fullscreen mode in tablet in non-multiWindow mode or with vertical video.
- // Other than that MATCH_PARENT is good
- final boolean navBarAtTheBottom = DeviceUtils.isTablet(service) || !isLandscape;
- controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow()
- && navBarAtTheBottom ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
- controlsRoot.requestLayout();
-
- final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
- int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
- topPadding = !isLandscape && DeviceUtils.hasCutout(topPadding, metrics) ? 0 : topPadding;
- getRootView().findViewById(R.id.playbackWindowRoot).setTranslationY(topPadding);
- getBottomControlsRoot().setTranslationY(-topPadding);
- }
-
- /**
- * @return statusBar height that was found inside system resources
- * or default value if no value was provided inside resources
- */
- private int getStatusBarHeight() {
- int statusBarHeight = 0;
- final int resourceId = service.isLandscape()
- ? service.getResources().getIdentifier(
- "status_bar_height_landscape", "dimen", "android")
- : service.getResources().getIdentifier(
- "status_bar_height", "dimen", "android");
-
- if (resourceId > 0) {
- statusBarHeight = service.getResources().getDimensionPixelSize(resourceId);
- }
- if (statusBarHeight == 0) {
- // Some devices provide wrong value for status bar height in landscape mode,
- // this is workaround
- final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
- statusBarHeight = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 24, metrics);
- }
- return statusBarHeight;
- }
-
protected void setMuteButton(final ImageButton button, final boolean isMuted) {
button.setImageDrawable(AppCompatResources.getDrawable(service, isMuted
? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp));
@@ -1617,8 +1531,6 @@ public class VideoPlayerImpl extends VideoPlayer
&& !DeviceUtils.isTablet(service)) {
toggleFullscreen();
}
-
- setControlsSize();
}
private void buildQueue() {
@@ -2015,6 +1927,9 @@ public class VideoPlayerImpl extends VideoPlayer
public void setFragmentListener(final PlayerServiceEventListener listener) {
fragmentListener = listener;
fragmentIsVisible = true;
+ // Prevent applying windows insets twice
+ getRootView().findViewById(R.id.playbackControlRoot).setPadding(0, 0, 0, 0);
+ queueLayout.setPadding(0, 0, 0, 0);
updateMetadata();
updatePlayback();
triggerProgressUpdate();
diff --git a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
index 7592d2f35..d852c2296 100644
--- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
@@ -6,8 +6,6 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.BatteryManager;
import android.os.Build;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@@ -74,17 +72,4 @@ public final class DeviceUtils {
return false;
}
}
-
- /*
- * Compares current status bar height with default status bar height in Android and decides,
- * does the device has cutout or not
- * */
- public static boolean hasCutout(final float statusBarHeight, final DisplayMetrics metrics) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- final float defaultStatusBarHeight = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 25, metrics);
- return statusBarHeight > defaultStatusBarHeight;
- }
- return false;
- }
}
diff --git a/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java b/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java
new file mode 100644
index 000000000..fcbdabc8b
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java
@@ -0,0 +1,36 @@
+package org.schabi.newpipe.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
+public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
+ public CustomCollapsingToolbarLayout(@NonNull final Context context) {
+ super(context);
+ overrideListener();
+ }
+
+ public CustomCollapsingToolbarLayout(@NonNull final Context context,
+ @Nullable final AttributeSet attrs) {
+ super(context, attrs);
+ overrideListener();
+ }
+
+ public CustomCollapsingToolbarLayout(@NonNull final Context context,
+ @Nullable final AttributeSet attrs,
+ final int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ overrideListener();
+ }
+
+ /**
+ * CollapsingToolbarLayout overrides our logic with fitsSystemWindows and ruins the layout.
+ * Override Google's method
+ * */
+ public void overrideListener() {
+ ViewCompat.setOnApplyWindowInsetsListener(this, (v, insets) -> insets);
+ }
+}
diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml
index 3c64be3b5..3a17104e4 100644
--- a/app/src/main/res/layout-large-land/fragment_video_detail.xml
+++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml
@@ -32,7 +32,7 @@
app:elevation="0dp"
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
-
@@ -161,7 +161,7 @@
-
+
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index bec810e6e..e6c78585e 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -21,7 +21,7 @@
app:elevation="0dp"
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
-
@@ -147,8 +147,7 @@
/>
-
-
+