Reimagined player positioning
This commit is contained in:
parent
6d38615ea8
commit
150e156d26
8 changed files with 90 additions and 142 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@
|
|||
app:elevation="0dp"
|
||||
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll">
|
||||
|
@ -161,7 +161,7 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
|
||||
|
||||
<!-- CONTENT -->
|
||||
<RelativeLayout
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
android:id="@+id/playbackControlRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="@color/video_overlay_color"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
app:elevation="0dp"
|
||||
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll">
|
||||
|
@ -147,8 +147,7 @@
|
|||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
|
||||
|
||||
<!-- CONTENT -->
|
||||
<RelativeLayout
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
android:id="@+id/playbackControlRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="@color/video_overlay_color"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
|
Loading…
Reference in a new issue