From 6d7e37610c80ee3a4a6918c595499e4f0d54054d Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Tue, 25 Feb 2020 02:15:22 +0300 Subject: [PATCH] Vertical videos in portrait & fullscreen, UI enhancements for tablets and phones, fixes - vertical videos now work ok in portrait and fullscreen mode at the same time - auto pause on back press is disabled for large tablets - large dragable area for swipe to bottom in fullscreen mode in place of top controls - appbar will be scrolled to top when entering in fullscreen mode --- .../fragments/detail/VideoDetailFragment.java | 11 +-- .../newpipe/player/VideoPlayerImpl.java | 75 ++++++++++++++----- .../player/event/PlayerGestureListener.java | 5 +- .../activity_main_player.xml | 44 +++++------ .../main/res/layout/activity_main_player.xml | 44 +++++------ 5 files changed, 112 insertions(+), 67 deletions(-) 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 6f65c34dc..a946e3f99 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 @@ -870,7 +870,7 @@ public class VideoDetailFragment // If we are in fullscreen mode just exit from it via first back press if (player != null && player.isInFullscreen()) { - player.onPause(); + if (!PlayerHelper.isTablet(activity)) player.onPause(); restoreDefaultOrientation(); setAutoplay(false); return true; @@ -1699,6 +1699,7 @@ public class VideoDetailFragment if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) return; currentInfo = info; + setInitialData(info.getServiceId(), info.getUrl(),info.getName(), playQueue); setAutoplay(false); prepareAndHandleInfo(info, true); } @@ -1736,6 +1737,7 @@ public class VideoDetailFragment } if (relatedStreamsLayout != null) relatedStreamsLayout.setVisibility(fullscreen ? View.GONE : View.VISIBLE); + scrollToTop(); addVideoPlayerView(); } @@ -1842,12 +1844,10 @@ public class VideoDetailFragment if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null) setAutoplay(true); + player.checkLandscape(); boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity); // Let's give a user time to look at video information page if video is not playing - if (player.isPlaying()) { - player.checkLandscape(); - } else if (orientationLocked) { - player.checkLandscape(); + if (orientationLocked && !player.isPlaying()) { player.onPlay(); player.showControlsThenHide(); } @@ -1927,6 +1927,7 @@ public class VideoDetailFragment case BottomSheetBehavior.STATE_COLLAPSED: // Re-enable clicks setOverlayElementsClickable(true); + if (player != null) player.onQueueClosed(); break; case BottomSheetBehavior.STATE_DRAGGING: case BottomSheetBehavior.STATE_SETTLING: 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 78f6aa387..ec490451b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -74,9 +74,11 @@ import org.schabi.newpipe.util.*; 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.*; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND; import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; +import static org.schabi.newpipe.player.helper.PlayerHelper.isTablet; import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA; import static org.schabi.newpipe.util.AnimationUtils.animateRotation; import static org.schabi.newpipe.util.AnimationUtils.animateView; @@ -137,6 +139,7 @@ public class VideoPlayerImpl extends VideoPlayer private boolean audioOnly = false; private boolean isFullscreen = false; + private boolean isVerticalVideo = false; boolean shouldUpdateOnProgress; private MainPlayer service; @@ -305,11 +308,13 @@ public class VideoPlayerImpl extends VideoPlayer openInBrowser.setVisibility(View.GONE); playerCloseButton.setVisibility(View.GONE); getTopControlsRoot().bringToFront(); + getTopControlsRoot().setClickable(false); + getTopControlsRoot().setFocusable(false); getBottomControlsRoot().bringToFront(); onQueueClosed(); } else { fullscreenButton.setVisibility(View.GONE); - setupScreenRotationButton(service.isLandscape()); + setupScreenRotationButton(); getResizeView().setVisibility(View.VISIBLE); getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE); moreOptionsButton.setVisibility(View.VISIBLE); @@ -323,6 +328,10 @@ public class VideoPlayerImpl extends VideoPlayer defaultPreferences.getBoolean(service.getString(R.string.show_play_with_kodi_key), false) ? View.VISIBLE : View.GONE); openInBrowser.setVisibility(View.VISIBLE); playerCloseButton.setVisibility(isFullscreen ? View.GONE : View.VISIBLE); + // Top controls have a large minHeight which is allows to drag the player down in fullscreen mode (just larger area + // to make easy to locate by finger) + getTopControlsRoot().setClickable(true); + getTopControlsRoot().setFocusable(true); } if (!isInFullscreen()) { titleTextView.setVisibility(View.GONE); @@ -393,7 +402,7 @@ public class VideoPlayerImpl extends VideoPlayer settingsContentObserver = new ContentObserver(new Handler()) { @Override - public void onChange(boolean selfChange) { setupScreenRotationButton(service.isLandscape()); } + public void onChange(boolean selfChange) { setupScreenRotationButton(); } }; service.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false, @@ -442,6 +451,14 @@ public class VideoPlayerImpl extends VideoPlayer setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence); } + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + super.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio); + isVerticalVideo = width < height; + prepareOrientation(); + setupScreenRotationButton(); + } + /*////////////////////////////////////////////////////////////////////////// // ExoPlayer Video Listener //////////////////////////////////////////////////////////////////////////*/ @@ -597,7 +614,7 @@ public class VideoPlayerImpl extends VideoPlayer channelTextView.setVisibility(View.VISIBLE); playerCloseButton.setVisibility(View.GONE); } - setupScreenRotationButton(isInFullscreen()); + setupScreenRotationButton(); } @Override @@ -637,7 +654,8 @@ public class VideoPlayerImpl extends VideoPlayer toggleFullscreen(); } else if (v.getId() == screenRotationButton.getId()) { - fragmentListener.onScreenRotationButtonClicked(); + if (!isVerticalVideo) fragmentListener.onScreenRotationButtonClicked(); + else toggleFullscreen(); } else if (v.getId() == playerCloseButton.getId()) { service.sendBroadcast(new Intent(VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER)); @@ -667,6 +685,7 @@ public class VideoPlayerImpl extends VideoPlayer private void onQueueClicked() { queueVisible = true; + hideSystemUIIfNeeded(); buildQueue(); updatePlaybackButtons(); @@ -677,7 +696,9 @@ public class VideoPlayerImpl extends VideoPlayer itemsList.scrollToPosition(playQueue.getIndex()); } - private void onQueueClosed() { + public void onQueueClosed() { + if (!queueVisible) return; + animateView(queueLayout, SLIDE_AND_ALPHA, /*visible=*/false, DEFAULT_CONTROLS_DURATION, 0, () -> { // Even when queueLayout is GONE it receives touch events and ruins normal behavior of the app. This line fixes it @@ -733,11 +754,18 @@ public class VideoPlayerImpl extends VideoPlayer builder.create().show(); } - private void setupScreenRotationButton(boolean landscape) { + private void setupScreenRotationButton() { boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service); - screenRotationButton.setVisibility(orientationLocked && videoPlayerSelected() ? View.VISIBLE : View.GONE); + boolean showButton = (orientationLocked || isVerticalVideo || isTablet(service)) && videoPlayerSelected(); + screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE); screenRotationButton.setImageDrawable(service.getResources().getDrawable( - landscape ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white)); + isInFullscreen() ? R.drawable.ic_fullscreen_exit_white : R.drawable.ic_fullscreen_white)); + } + + private void prepareOrientation() { + boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service); + if (orientationLocked && isInFullscreen() && service.isLandscape() == isVerticalVideo && fragmentListener != null) + fragmentListener.onScreenRotationButtonClicked(); } @Override @@ -779,7 +807,7 @@ public class VideoPlayerImpl extends VideoPlayer brightnessProgressBar.setMax(maxGestureLength); setInitialGestureValues(); - queueLayout.getLayoutParams().height = min - queueLayout.getTop(); + queueLayout.getLayoutParams().height = height - queueLayout.getTop(); if (popupPlayerSelected()) { float widthDp = Math.abs(r - l) / service.getResources().getDisplayMetrics().density; @@ -1009,7 +1037,16 @@ public class VideoPlayerImpl extends VideoPlayer onRepeatClicked(); break; case Intent.ACTION_CONFIGURATION_CHANGED: - setControlsSize(); + // 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 + boolean reportedOrientationIsLandscape = service.isLandscape(); + 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(); break; case Intent.ACTION_SCREEN_ON: shouldUpdateOnProgress = true; @@ -1201,7 +1238,7 @@ public class VideoPlayerImpl extends VideoPlayer // It doesn't include NavigationBar, notches, etc. display.getSize(size); - int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT; + int width = isFullscreen ? (service.isLandscape() ? size.x : size.y) : ViewGroup.LayoutParams.MATCH_PARENT; int gravity = isFullscreen ? (display.getRotation() == Surface.ROTATION_90 ? Gravity.START : Gravity.END) : Gravity.TOP; getTopControlsRoot().getLayoutParams().width = width; @@ -1218,10 +1255,11 @@ public class VideoPlayerImpl extends VideoPlayer bottomParams.addRule(gravity == Gravity.END ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_START); getBottomControlsRoot().requestLayout(); - ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackControlRoot); - // In tablet navigationBar located at the bottom of the screen. And the only situation when we need to set custom height is - // in fullscreen mode in tablet in non-multiWindow mode. Other than that MATCH_PARENT is good - controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && PlayerHelper.isTablet(service) + 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 + boolean navBarAtTheBottom = PlayerHelper.isTablet(service) || !service.isLandscape(); + controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow() && navBarAtTheBottom ? size.y : ViewGroup.LayoutParams.MATCH_PARENT; controlsRoot.requestLayout(); @@ -1264,9 +1302,12 @@ public class VideoPlayerImpl extends VideoPlayer public void checkLandscape() { AppCompatActivity parent = getParentActivity(); - if (parent != null && service.isLandscape() != isInFullscreen() - && getCurrentState() != STATE_COMPLETED && videoPlayerSelected() && !audioOnly) + boolean videoInLandscapeButNotInFullscreen = service.isLandscape() && !isInFullscreen() && videoPlayerSelected() && !audioOnly; + boolean playingState = getCurrentState() != STATE_COMPLETED && getCurrentState() != STATE_PAUSED; + if (parent != null && videoInLandscapeButNotInFullscreen && playingState) toggleFullscreen(); + + setControlsSize(); } private void buildQueue() { diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java index db7d8d615..bb9ede4d7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java @@ -160,9 +160,8 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen isMovingInMain = true; - boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled; - boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2; - boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea; + boolean acceptVolumeArea = initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0; + boolean acceptBrightnessArea = initialEvent.getX() <= playerImpl.getRootView().getWidth() / 2.0; if (isVolumeGestureEnabled && acceptVolumeArea) { playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY); diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml index 4a4879d7f..d87930371 100644 --- a/app/src/main/res/layout-large-land/activity_main_player.xml +++ b/app/src/main/res/layout-large-land/activity_main_player.xml @@ -12,7 +12,7 @@ android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_centerHorizontal="true"/> + android:layout_centerInParent="true"/> + + - - - + + android:layout_centerInParent="true"/> + + - - - +