From 4519dd010dcc8ac8791d97046363481d9afebe72 Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Fri, 3 Jan 2020 08:05:31 +0300 Subject: [PATCH] Second block of fixes for review - hide/show controls with respect of SystemUI. In fullscreen mode controls will stay away from NavigationBar - notification from running service will be hidden if a user disabled background playback - fixed incorrect handling of a system method in API 19 - better MultiWindow support --- .../fragments/detail/VideoDetailFragment.java | 28 ++++++------ .../org/schabi/newpipe/player/MainPlayer.java | 7 ++- .../schabi/newpipe/player/VideoPlayer.java | 10 ++--- .../newpipe/player/VideoPlayerImpl.java | 45 ++++++++++++++++--- .../event/CustomBottomSheetBehavior.java | 21 +++++---- .../player/event/PlayerGestureListener.java | 8 ---- .../activity_main_player.xml | 7 ++- .../main/res/layout/activity_main_player.xml | 7 ++- .../main/res/layout/fragment_video_detail.xml | 4 +- 9 files changed, 87 insertions(+), 50 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 d0ca95d6b..8813d139d 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 @@ -1244,7 +1244,7 @@ public class VideoDetailFragment int height; if (player != null && player.isInFullscreen()) - height = activity.getWindow().getDecorView().getHeight(); + height = isInMultiWindow() ? getView().getHeight() : activity.getWindow().getDecorView().getHeight(); else height = isPortrait ? (int) (metrics.widthPixels / (16.0f / 9.0f)) @@ -1669,8 +1669,7 @@ public class VideoDetailFragment player.useVideoSource(false); else if (player.minimizeOnPopupEnabled()) NavigationHelper.playOnPopupPlayer(activity, playQueue, true); - else - player.getPlayer().setPlayWhenReady(false); + else player.onPause(); } else player.useVideoSource(true); } @@ -1751,23 +1750,18 @@ public class VideoDetailFragment getActivity().getWindow().getDecorView().setSystemUiVisibility(0); getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } private void hideSystemUi() { if (DEBUG) Log.d(TAG, "hideSystemUi() called"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - 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; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - visibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - activity.getWindow().getDecorView().setSystemUiVisibility(visibility); - } + 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; + activity.getWindow().getDecorView().setSystemUiVisibility(visibility); activity.getWindow().setFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } @@ -1813,6 +1807,10 @@ public class VideoDetailFragment return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels; } + private boolean isInMultiWindow() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode(); + } + /* * Means that the player fragment was swiped away via BottomSheetLayout and is empty but ready for any new actions. See cleanUp() * */ diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index a0d4eb7d9..6e5082494 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; +import android.util.DisplayMetrics; import android.view.ViewGroup; import android.view.WindowManager; import androidx.core.app.NotificationCompat; @@ -183,7 +184,11 @@ public final class MainPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ boolean isLandscape() { - return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels; + // DisplayMetrics from activity context knows about MultiWindow feature while DisplayMetrics from app context doesn't + final DisplayMetrics metrics = playerImpl.getParentActivity() != null ? + playerImpl.getParentActivity().getResources().getDisplayMetrics() + : getResources().getDisplayMetrics(); + return metrics.heightPixels < metrics.widthPixels; } public View getView() { diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index 3a906d80c..07f485e7a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -38,11 +38,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceView; import android.view.View; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.ProgressBar; -import android.widget.SeekBar; -import android.widget.TextView; +import android.widget.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -131,7 +127,7 @@ public abstract class VideoPlayer extends BasePlayer private TextView playbackLiveSync; private TextView playbackSpeedTextView; - private View topControlsRoot; + private LinearLayout topControlsRoot; private TextView qualityTextView; private SubtitleView subtitleView; @@ -961,7 +957,7 @@ public abstract class VideoPlayer extends BasePlayer return playbackEndTime; } - public View getTopControlsRoot() { + public LinearLayout getTopControlsRoot() { return topControlsRoot; } 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 71fe21b6d..76c8106be 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -25,6 +25,7 @@ import android.annotation.SuppressLint; import android.content.*; import android.graphics.Bitmap; import android.graphics.PixelFormat; +import android.graphics.Point; import android.net.Uri; import android.os.Build; import android.preference.PreferenceManager; @@ -124,7 +125,6 @@ public class VideoPlayerImpl extends VideoPlayer private ImageButton shareButton; private View primaryControls; - private LinearLayout topControls; private View secondaryControls; private int maxGestureLength; @@ -245,7 +245,6 @@ public class VideoPlayerImpl extends VideoPlayer this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton); this.primaryControls = rootView.findViewById(R.id.primaryControls); - this.topControls = rootView.findViewById(R.id.topControls); this.secondaryControls = rootView.findViewById(R.id.secondaryControls); this.shareButton = rootView.findViewById(R.id.share); @@ -285,7 +284,7 @@ public class VideoPlayerImpl extends VideoPlayer getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE); queueButton.setVisibility(View.GONE); moreOptionsButton.setVisibility(View.GONE); - topControls.setOrientation(LinearLayout.HORIZONTAL); + getTopControlsRoot().setOrientation(LinearLayout.HORIZONTAL); primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT; secondaryControls.setAlpha(1f); secondaryControls.setVisibility(View.VISIBLE); @@ -297,7 +296,7 @@ public class VideoPlayerImpl extends VideoPlayer fullscreenButton.setVisibility(View.GONE); getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE); moreOptionsButton.setVisibility(View.VISIBLE); - topControls.setOrientation(LinearLayout.VERTICAL); + getTopControlsRoot().setOrientation(LinearLayout.VERTICAL); primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT; secondaryControls.setVisibility(View.GONE); moreOptionsButton.setImageDrawable(service.getResources().getDrawable( @@ -507,7 +506,7 @@ public class VideoPlayerImpl extends VideoPlayer @Override public void toggleFullscreen() { - if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); + if (DEBUG) Log.d(TAG, "toggleFullscreen() called"); if (simpleExoPlayer == null || getCurrentMetadata() == null) return; if (popupPlayerSelected()) { @@ -535,6 +534,7 @@ public class VideoPlayerImpl extends VideoPlayer if (fragmentListener == null) return; isFullscreen = !isFullscreen; + setControlsWidth(); fragmentListener.onFullscreenStateChanged(isInFullscreen()); // When user presses back button in landscape mode and in fullscreen and uses ZOOM mode // a video can be larger than screen. Prevent it like this @@ -595,7 +595,8 @@ public class VideoPlayerImpl extends VideoPlayer getControlsVisibilityHandler().removeCallbacksAndMessages(null); animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> { if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) { - hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); + if (v.getId() == playPauseButton.getId()) hideControls(0, 0); + else hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); } }); } @@ -816,6 +817,8 @@ public class VideoPlayerImpl extends VideoPlayer service.getLockManager().acquireWifiAndCpu(); service.resetNotification(); service.updateNotification(R.drawable.ic_pause_white); + + service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build()); } @Override @@ -831,6 +834,10 @@ public class VideoPlayerImpl extends VideoPlayer service.resetNotification(); service.updateNotification(R.drawable.ic_play_arrow_white); + // Remove running notification when user don't want music (or video in popup) to be played in background + if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected()) + service.stopForeground(true); + getRootView().setKeepScreenOn(false); service.getLockManager().releaseWifiAndCpu(); @@ -1033,6 +1040,7 @@ public class VideoPlayerImpl extends VideoPlayer if (queueVisible) return; showOrHideButtons(); + showSystemUIPartially(); super.showControlsThenHide(); } @@ -1041,6 +1049,7 @@ public class VideoPlayerImpl extends VideoPlayer if (queueVisible) return; showOrHideButtons(); + showSystemUIPartially(); super.showControls(duration); } @@ -1078,12 +1087,36 @@ public class VideoPlayerImpl extends VideoPlayer queueButton.setVisibility(View.VISIBLE); } + private void showSystemUIPartially() { + if (isInFullscreen()) { + int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN; + getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility); + } + } + @Override public void hideSystemUIIfNeeded() { if (fragmentListener != null) fragmentListener.hideSystemUIIfNeeded(); } + private void setControlsWidth() { + Point size = new Point(); + // This method will give a correct size of a usable area of a window. + // It doesn't include NavigationBar, notches, etc. + getRootView().getDisplay().getSize(size); + + int width = isFullscreen ? size.x : ViewGroup.LayoutParams.MATCH_PARENT; + primaryControls.getLayoutParams().width = width; + primaryControls.requestLayout(); + secondaryControls.getLayoutParams().width = width; + secondaryControls.requestLayout(); + getBottomControlsRoot().getLayoutParams().width = width; + getBottomControlsRoot().requestLayout(); + } + private void updatePlaybackButtons() { if (repeatButton == null || shuffleButton == null || simpleExoPlayer == null || playQueue == null) return; diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java index 85db3b201..6f4cf41de 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java @@ -18,27 +18,30 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior { @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) { + // Behavior of globalVisibleRect is different on different APIs. + // For example, on API 19 getGlobalVisibleRect returns a filled rect of a collapsed view while on the latest API + // it returns empty rect in that case. So check visibility with return value too + boolean visible; + Rect rect = new Rect(); + // Without overriding scrolling will not work in detail_content_root_layout ViewGroup controls = child.findViewById(R.id.detail_content_root_layout); if (controls != null) { - Rect rect = new Rect(); - controls.getGlobalVisibleRect(rect); - if (rect.contains((int) event.getX(), (int) event.getY())) return false; + visible = controls.getGlobalVisibleRect(rect); + if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false; } // Without overriding scrolling will not work on relatedStreamsLayout ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout); if (relatedStreamsLayout != null) { - Rect rect = new Rect(); - relatedStreamsLayout.getGlobalVisibleRect(rect); - if (rect.contains((int) event.getX(), (int) event.getY())) return false; + visible = relatedStreamsLayout.getGlobalVisibleRect(rect); + if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false; } ViewGroup playQueue = child.findViewById(R.id.playQueue); if (playQueue != null) { - Rect rect = new Rect(); - playQueue.getGlobalVisibleRect(rect); - if (rect.contains((int) event.getX(), (int) event.getY())) return false; + visible = playQueue.getGlobalVisibleRect(rect); + if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false; } return super.onInterceptTouchEvent(parent, child, event); 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 00a511ca3..db7d8d615 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 @@ -139,14 +139,6 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen } else { playerImpl.showControlsThenHide(); } - if (playerImpl.isInFullscreen()) { - int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_FULLSCREEN; - playerImpl.getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility); - playerImpl.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - playerImpl.getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - } } return true; } 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 8d7d9b639..ff57bc2bf 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 @@ -377,12 +377,17 @@ android:visibility="gone" tools:visibility="visible"/> + + + +