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
This commit is contained in:
Avently 2020-01-03 08:05:31 +03:00
parent bc2dc8d933
commit 4519dd010d
9 changed files with 87 additions and 50 deletions

View file

@ -1244,7 +1244,7 @@ public class VideoDetailFragment
int height; int height;
if (player != null && player.isInFullscreen()) if (player != null && player.isInFullscreen())
height = activity.getWindow().getDecorView().getHeight(); height = isInMultiWindow() ? getView().getHeight() : activity.getWindow().getDecorView().getHeight();
else else
height = isPortrait height = isPortrait
? (int) (metrics.widthPixels / (16.0f / 9.0f)) ? (int) (metrics.widthPixels / (16.0f / 9.0f))
@ -1669,8 +1669,7 @@ public class VideoDetailFragment
player.useVideoSource(false); player.useVideoSource(false);
else if (player.minimizeOnPopupEnabled()) else if (player.minimizeOnPopupEnabled())
NavigationHelper.playOnPopupPlayer(activity, playQueue, true); NavigationHelper.playOnPopupPlayer(activity, playQueue, true);
else else player.onPause();
player.getPlayer().setPlayWhenReady(false);
} }
else player.useVideoSource(true); else player.useVideoSource(true);
} }
@ -1751,23 +1750,18 @@ public class VideoDetailFragment
getActivity().getWindow().getDecorView().setSystemUiVisibility(0); getActivity().getWindow().getDecorView().setSystemUiVisibility(0);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} }
private void hideSystemUi() { private void hideSystemUi() {
if (DEBUG) Log.d(TAG, "hideSystemUi() called"); 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
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
visibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
}
activity.getWindow().setFlags( activity.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 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; 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() * Means that the player fragment was swiped away via BottomSheetLayout and is empty but ready for any new actions. See cleanUp()
* */ * */

View file

@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.util.DisplayMetrics;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
@ -183,7 +184,11 @@ public final class MainPlayer extends Service {
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
boolean isLandscape() { 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() { public View getView() {

View file

@ -38,11 +38,7 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.*;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -131,7 +127,7 @@ public abstract class VideoPlayer extends BasePlayer
private TextView playbackLiveSync; private TextView playbackLiveSync;
private TextView playbackSpeedTextView; private TextView playbackSpeedTextView;
private View topControlsRoot; private LinearLayout topControlsRoot;
private TextView qualityTextView; private TextView qualityTextView;
private SubtitleView subtitleView; private SubtitleView subtitleView;
@ -961,7 +957,7 @@ public abstract class VideoPlayer extends BasePlayer
return playbackEndTime; return playbackEndTime;
} }
public View getTopControlsRoot() { public LinearLayout getTopControlsRoot() {
return topControlsRoot; return topControlsRoot;
} }

View file

@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
import android.content.*; import android.content.*;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
@ -124,7 +125,6 @@ public class VideoPlayerImpl extends VideoPlayer
private ImageButton shareButton; private ImageButton shareButton;
private View primaryControls; private View primaryControls;
private LinearLayout topControls;
private View secondaryControls; private View secondaryControls;
private int maxGestureLength; private int maxGestureLength;
@ -245,7 +245,6 @@ public class VideoPlayerImpl extends VideoPlayer
this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton); this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton);
this.primaryControls = rootView.findViewById(R.id.primaryControls); this.primaryControls = rootView.findViewById(R.id.primaryControls);
this.topControls = rootView.findViewById(R.id.topControls);
this.secondaryControls = rootView.findViewById(R.id.secondaryControls); this.secondaryControls = rootView.findViewById(R.id.secondaryControls);
this.shareButton = rootView.findViewById(R.id.share); this.shareButton = rootView.findViewById(R.id.share);
@ -285,7 +284,7 @@ public class VideoPlayerImpl extends VideoPlayer
getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE); getRootView().findViewById(R.id.metadataView).setVisibility(View.GONE);
queueButton.setVisibility(View.GONE); queueButton.setVisibility(View.GONE);
moreOptionsButton.setVisibility(View.GONE); moreOptionsButton.setVisibility(View.GONE);
topControls.setOrientation(LinearLayout.HORIZONTAL); getTopControlsRoot().setOrientation(LinearLayout.HORIZONTAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT; primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.WRAP_CONTENT;
secondaryControls.setAlpha(1f); secondaryControls.setAlpha(1f);
secondaryControls.setVisibility(View.VISIBLE); secondaryControls.setVisibility(View.VISIBLE);
@ -297,7 +296,7 @@ public class VideoPlayerImpl extends VideoPlayer
fullscreenButton.setVisibility(View.GONE); fullscreenButton.setVisibility(View.GONE);
getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE); getRootView().findViewById(R.id.metadataView).setVisibility(View.VISIBLE);
moreOptionsButton.setVisibility(View.VISIBLE); moreOptionsButton.setVisibility(View.VISIBLE);
topControls.setOrientation(LinearLayout.VERTICAL); getTopControlsRoot().setOrientation(LinearLayout.VERTICAL);
primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT; primaryControls.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT;
secondaryControls.setVisibility(View.GONE); secondaryControls.setVisibility(View.GONE);
moreOptionsButton.setImageDrawable(service.getResources().getDrawable( moreOptionsButton.setImageDrawable(service.getResources().getDrawable(
@ -507,7 +506,7 @@ public class VideoPlayerImpl extends VideoPlayer
@Override @Override
public void toggleFullscreen() { public void toggleFullscreen() {
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); if (DEBUG) Log.d(TAG, "toggleFullscreen() called");
if (simpleExoPlayer == null || getCurrentMetadata() == null) return; if (simpleExoPlayer == null || getCurrentMetadata() == null) return;
if (popupPlayerSelected()) { if (popupPlayerSelected()) {
@ -535,6 +534,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (fragmentListener == null) return; if (fragmentListener == null) return;
isFullscreen = !isFullscreen; isFullscreen = !isFullscreen;
setControlsWidth();
fragmentListener.onFullscreenStateChanged(isInFullscreen()); fragmentListener.onFullscreenStateChanged(isInFullscreen());
// When user presses back button in landscape mode and in fullscreen and uses ZOOM mode // 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 // a video can be larger than screen. Prevent it like this
@ -595,7 +595,8 @@ public class VideoPlayerImpl extends VideoPlayer
getControlsVisibilityHandler().removeCallbacksAndMessages(null); getControlsVisibilityHandler().removeCallbacksAndMessages(null);
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> { animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) { 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.getLockManager().acquireWifiAndCpu();
service.resetNotification(); service.resetNotification();
service.updateNotification(R.drawable.ic_pause_white); service.updateNotification(R.drawable.ic_pause_white);
service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
} }
@Override @Override
@ -831,6 +834,10 @@ public class VideoPlayerImpl extends VideoPlayer
service.resetNotification(); service.resetNotification();
service.updateNotification(R.drawable.ic_play_arrow_white); 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); getRootView().setKeepScreenOn(false);
service.getLockManager().releaseWifiAndCpu(); service.getLockManager().releaseWifiAndCpu();
@ -1033,6 +1040,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (queueVisible) return; if (queueVisible) return;
showOrHideButtons(); showOrHideButtons();
showSystemUIPartially();
super.showControlsThenHide(); super.showControlsThenHide();
} }
@ -1041,6 +1049,7 @@ public class VideoPlayerImpl extends VideoPlayer
if (queueVisible) return; if (queueVisible) return;
showOrHideButtons(); showOrHideButtons();
showSystemUIPartially();
super.showControls(duration); super.showControls(duration);
} }
@ -1078,12 +1087,36 @@ public class VideoPlayerImpl extends VideoPlayer
queueButton.setVisibility(View.VISIBLE); 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 @Override
public void hideSystemUIIfNeeded() { public void hideSystemUIIfNeeded() {
if (fragmentListener != null) if (fragmentListener != null)
fragmentListener.hideSystemUIIfNeeded(); 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() { private void updatePlaybackButtons() {
if (repeatButton == null || shuffleButton == null || if (repeatButton == null || shuffleButton == null ||
simpleExoPlayer == null || playQueue == null) return; simpleExoPlayer == null || playQueue == null) return;

View file

@ -18,27 +18,30 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior {
@Override @Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) { 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 // Without overriding scrolling will not work in detail_content_root_layout
ViewGroup controls = child.findViewById(R.id.detail_content_root_layout); ViewGroup controls = child.findViewById(R.id.detail_content_root_layout);
if (controls != null) { if (controls != null) {
Rect rect = new Rect(); visible = controls.getGlobalVisibleRect(rect);
controls.getGlobalVisibleRect(rect); if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
if (rect.contains((int) event.getX(), (int) event.getY())) return false;
} }
// Without overriding scrolling will not work on relatedStreamsLayout // Without overriding scrolling will not work on relatedStreamsLayout
ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout); ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout);
if (relatedStreamsLayout != null) { if (relatedStreamsLayout != null) {
Rect rect = new Rect(); visible = relatedStreamsLayout.getGlobalVisibleRect(rect);
relatedStreamsLayout.getGlobalVisibleRect(rect); if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
if (rect.contains((int) event.getX(), (int) event.getY())) return false;
} }
ViewGroup playQueue = child.findViewById(R.id.playQueue); ViewGroup playQueue = child.findViewById(R.id.playQueue);
if (playQueue != null) { if (playQueue != null) {
Rect rect = new Rect(); visible = playQueue.getGlobalVisibleRect(rect);
playQueue.getGlobalVisibleRect(rect); if (rect.contains((int) event.getX(), (int) event.getY()) && visible) return false;
if (rect.contains((int) event.getX(), (int) event.getY())) return false;
} }
return super.onInterceptTouchEvent(parent, child, event); return super.onInterceptTouchEvent(parent, child, event);

View file

@ -139,14 +139,6 @@ public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListen
} else { } else {
playerImpl.showControlsThenHide(); 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; return true;
} }

View file

@ -377,12 +377,17 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible"/>
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_bg"
android:layout_alignParentBottom="true" />
<LinearLayout <LinearLayout
android:id="@+id/bottomControls" android:id="@+id/bottomControls"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="@drawable/player_controls_bg"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingBottom="6dp" android:paddingBottom="6dp"

View file

@ -375,12 +375,17 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible"/>
<View
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/player_controls_bg"
android:layout_alignParentBottom="true" />
<LinearLayout <LinearLayout
android:id="@+id/bottomControls" android:id="@+id/bottomControls"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="@drawable/player_controls_bg"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingBottom="6dp" android:paddingBottom="6dp"

View file

@ -549,11 +549,10 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:alpha="0" android:alpha="0"
tools:alpha="1" tools:alpha="1"
android:paddingLeft="@dimen/video_item_search_padding"
android:paddingRight="@dimen/video_item_search_padding"
android:background="?attr/windowBackground" > android:background="?attr/windowBackground" >
<ImageButton <ImageButton
android:paddingLeft="@dimen/video_item_search_padding"
android:id="@+id/overlay_thumbnail" android:id="@+id/overlay_thumbnail"
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="60dp" android:layout_height="60dp"
@ -613,6 +612,7 @@
android:layout_height="60dp" android:layout_height="60dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingLeft="@dimen/video_item_search_padding" android:paddingLeft="@dimen/video_item_search_padding"
android:paddingRight="@dimen/video_item_search_padding"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
tools:ignore="RtlHardcoded"> tools:ignore="RtlHardcoded">