Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to directly open fullscreen when the main player starts #5459

Merged
merged 8 commits into from
Sep 1, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public final class VideoDetailFragment
@Nullable
private MainPlayer playerService;
private Player player;
private PlayerHolder playerHolder = PlayerHolder.getInstance();
private final PlayerHolder playerHolder = PlayerHolder.getInstance();

/*//////////////////////////////////////////////////////////////////////////
// Service management
Expand All @@ -220,7 +220,7 @@ public void onServiceConnected(final Player connectedPlayer,
return;
}

if (isLandscape()) {
if (DeviceUtils.isLandscape(requireContext())) {
// If the video is playing but orientation changed
// let's make the video in fullscreen again
checkLandscape();
Expand All @@ -241,7 +241,7 @@ public void onServiceConnected(final Player connectedPlayer,
&& isAutoplayEnabled()
&& player.getParentActivity() == null)) {
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer();
openVideoPlayerAutoFullscreen();
}
}

Expand Down Expand Up @@ -499,7 +499,7 @@ public void onClick(final View v) {
break;
case R.id.detail_thumbnail_root_layout:
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer();
openVideoPlayerAutoFullscreen();
break;
case R.id.detail_title_root_layout:
toggleTitleAndSecondaryControls();
Expand All @@ -516,7 +516,7 @@ public void onClick(final View v) {
showSystemUi();
} else {
autoPlayEnabled = true; // forcefully start playing
openVideoPlayer();
openVideoPlayer(false);
}

setOverlayPlayPauseImage(isPlayerAvailable() && player.isPlaying());
Expand Down Expand Up @@ -762,7 +762,7 @@ public boolean onBackPressed() {

private void setupFromHistoryItem(final StackItem item) {
setAutoPlay(false);
hideMainPlayer();
hideMainPlayerOnLoadingNewStream();

setInitialData(item.getServiceId(), item.getUrl(),
item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue());
Expand Down Expand Up @@ -882,7 +882,7 @@ private void runWorker(final boolean forceLoad, final boolean addToBackStack) {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
isLoading.set(false);
hideMainPlayer();
hideMainPlayerOnLoadingNewStream();
if (result.getAgeLimit() != NO_AGE_LIMIT && !prefs.getBoolean(
getString(R.string.show_age_restricted_content), false)) {
hideAgeRestrictedContent();
Expand All @@ -897,8 +897,9 @@ private void runWorker(final boolean forceLoad, final boolean addToBackStack) {
stack.push(new StackItem(serviceId, url, title, playQueue));
}
}

if (isAutoplayEnabled()) {
openVideoPlayer();
openVideoPlayerAutoFullscreen();
}
}
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
Expand Down Expand Up @@ -1103,7 +1104,29 @@ private void openPopupPlayer(final boolean append) {
}
}

public void openVideoPlayer() {
/**
* Opens the video player, in fullscreen if needed. In order to open fullscreen, the activity
* is toggled to landscape orientation (which will then cause fullscreen mode).
*
* @param directlyFullscreenIfApplicable whether to open fullscreen if we are not already
* in landscape and screen orientation is locked
*/
public void openVideoPlayer(final boolean directlyFullscreenIfApplicable) {
if (directlyFullscreenIfApplicable
&& !DeviceUtils.isLandscape(requireContext())
&& PlayerHelper.globalScreenOrientationLocked(requireContext())) {
// Make sure the bottom sheet turns out expanded. When this code kicks in the bottom
// sheet could not have fully expanded yet, and thus be in the STATE_SETTLING state.
// When the activity is rotated, and its state is saved and then restored, the bottom
// sheet would forget what it was doing, since even if STATE_SETTLING is restored, it
// doesn't tell which state it was settling to, and thus the bottom sheet settles to
// STATE_COLLAPSED. This can be solved by manually setting the state that will be
// restored (i.e. bottomSheetState) to STATE_EXPANDED.
bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
// toggle landscape in order to open directly in fullscreen
onScreenRotationButtonClicked();
}

if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
showExternalPlaybackDialog();
Expand All @@ -1112,6 +1135,18 @@ public void openVideoPlayer() {
}
}

/**
* If the option to start directly fullscreen is enabled, calls
* {@link #openVideoPlayer(boolean)} with {@code directlyFullscreenIfApplicable = true}, so that
* if the user is not already in landscape and he has screen orientation locked the activity
* rotates and fullscreen starts. Otherwise, if the option to start directly fullscreen is
* disabled, calls {@link #openVideoPlayer(boolean)} with {@code directlyFullscreenIfApplicable
* = false}, hence preventing it from going directly fullscreen.
*/
public void openVideoPlayerAutoFullscreen() {
openVideoPlayer(PlayerHelper.isStartMainPlayerFullscreenEnabled(requireContext()));
}

private void openNormalBackgroundPlayer(final boolean append) {
// See UI changes while remote playQueue changes
if (!isPlayerAvailable()) {
Expand Down Expand Up @@ -1145,21 +1180,32 @@ private void openMainPlayer() {
}
addVideoPlayerView();

final Intent playerIntent = NavigationHelper
.getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled);
final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
MainPlayer.class, queue, true, autoPlayEnabled);
ContextCompat.startForegroundService(activity, playerIntent);
}

private void hideMainPlayer() {
/**
* When the video detail fragment is already showing details for a video and the user opens a
* new one, the video detail fragment changes all of its old data to the new stream, so if there
* is a video player currently open it should be hidden. This method does exactly that. If
* autoplay is enabled, the underlying player is not stopped completely, since it is going to
* be reused in a few milliseconds and the flickering would be annoying.
*/
private void hideMainPlayerOnLoadingNewStream() {
if (!isPlayerServiceAvailable()
|| playerService.getView() == null
|| !player.videoPlayerSelected()) {
return;
}

removeVideoPlayerView();
playerService.stop(isAutoplayEnabled());
playerService.getView().setVisibility(View.GONE);
if (isAutoplayEnabled()) {
playerService.stopForImmediateReusing();
playerService.getView().setVisibility(View.GONE);
} else {
playerHolder.stopService();
}
}

private PlayQueue setupPlayQueueForIntent(final boolean append) {
Expand Down Expand Up @@ -1252,7 +1298,7 @@ public boolean onPreDraw() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();

if (getView() != null) {
final int height = (isInMultiWindow()
final int height = (DeviceUtils.isInMultiWindow(activity)
? requireView()
: activity.getWindow().getDecorView()).getHeight();
setHeightThumbnail(height, metrics);
Expand All @@ -1275,7 +1321,7 @@ private void setHeightThumbnail() {
requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);

if (isPlayerAvailable() && player.isFullscreen()) {
final int height = (isInMultiWindow()
final int height = (DeviceUtils.isInMultiWindow(activity)
? requireView()
: activity.getWindow().getDecorView()).getHeight();
// Height is zero when the view is not yet displayed like after orientation change
Expand Down Expand Up @@ -1808,7 +1854,7 @@ public void onPlayerError(final ExoPlaybackException error) {
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
// Properly exit from fullscreen
toggleFullscreenIfInFullscreenMode();
hideMainPlayer();
hideMainPlayerOnLoadingNewStream();
}
}

Expand Down Expand Up @@ -1864,13 +1910,14 @@ public void onScreenRotationButtonClicked() {
// from landscape to portrait every time.
// Just turn on fullscreen mode in landscape orientation
// or portrait & unlocked global orientation
final boolean isLandscape = DeviceUtils.isLandscape(requireContext());
if (DeviceUtils.isTablet(activity)
&& (!globalScreenOrientationLocked(activity) || isLandscape())) {
&& (!globalScreenOrientationLocked(activity) || isLandscape)) {
player.toggleFullscreen();
return;
}

final int newOrientation = isLandscape()
final int newOrientation = isLandscape
? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;

Expand Down Expand Up @@ -1942,15 +1989,17 @@ private void hideSystemUi() {
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| 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()) {
final boolean isInMultiWindow = DeviceUtils.isInMultiWindow(activity);
if (!isInMultiWindow) {
visibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& (isInMultiWindow() || (isPlayerAvailable() && player.isFullscreen()))) {
&& (isInMultiWindow || (isPlayerAvailable() && player.isFullscreen()))) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
Expand Down Expand Up @@ -2022,15 +2071,6 @@ private void checkLandscape() {
}
}

private boolean isLandscape() {
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()
Expand Down Expand Up @@ -2213,7 +2253,7 @@ public void onStateChanged(@NonNull final View bottomSheet, final int newState)
setOverlayElementsClickable(false);
hideSystemUiIfNeeded();
// Conditions when the player should be expanded to fullscreen
if (isLandscape()
if (DeviceUtils.isLandscape(requireContext())
&& isPlayerAvailable()
&& player.isPlaying()
&& !player.isFullscreen()
Expand Down
22 changes: 8 additions & 14 deletions app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -36,6 +35,7 @@

import org.schabi.newpipe.App;
import org.schabi.newpipe.databinding.PlayerBinding;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ThemeHelper;

import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
Expand Down Expand Up @@ -133,32 +133,29 @@ public int onStartCommand(final Intent intent, final int flags, final int startI
return START_NOT_STICKY;
}

public void stop(final boolean autoplayEnabled) {
public void stopForImmediateReusing() {
if (DEBUG) {
Log.d(TAG, "stop() called");
Log.d(TAG, "stopForImmediateReusing() called");
}

if (!player.exoPlayerIsNull()) {
player.saveWasPlaying();

// Releases wifi & cpu, disables keepScreenOn, etc.
if (!autoplayEnabled) {
player.pause();
}
// We can't just pause the player here because it will make transition
// from one stream to a new stream not smooth
player.smoothStopPlayer();
player.setRecovery();

// Android TV will handle back button in case controls will be visible
// (one more additional unneeded click while the player is hidden)
player.hideControls(0, 0);
player.closeItemsList();

// Notification shows information about old stream but if a user selects
// a stream from backStack it's not actual anymore
// So we should hide the notification at all.
// When autoplay enabled such notification flashing is annoying so skip this case
if (!autoplayEnabled) {
NotificationUtil.getInstance().cancelNotificationAndStopForeground(this);
}
}
}

Expand Down Expand Up @@ -222,11 +219,8 @@ public IBinder onBind(final Intent intent) {
boolean isLandscape() {
// DisplayMetrics from activity context knows about MultiWindow feature
// while DisplayMetrics from app context doesn't
final DisplayMetrics metrics = (player != null
&& player.getParentActivity() != null
? player.getParentActivity().getResources()
: getResources()).getDisplayMetrics();
return metrics.heightPixels < metrics.widthPixels;
return DeviceUtils.isLandscape(player != null && player.getParentActivity() != null
? player.getParentActivity() : this);
}

@Nullable
Expand Down
24 changes: 21 additions & 3 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,9 @@ public void handleIntent(@NonNull final Intent intent) {
return;
}

// needed for tablets, check the function for a better explanation
directlyOpenFullscreenIfNeeded();

final PlaybackParameters savedParameters = retrievePlaybackParametersFromPrefs(this);
final float playbackSpeed = savedParameters.speed;
final float playbackPitch = savedParameters.pitch;
Expand Down Expand Up @@ -672,6 +675,7 @@ public void handleIntent(@NonNull final Intent intent) {
&& isPlaybackResumeEnabled(this)
&& !samePlayQueue
&& !newQueue.isEmpty()
&& newQueue.getItem() != null
&& newQueue.getItem().getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET) {
databaseUpdateDisposable.add(recordManager.loadStreamState(newQueue.getItem())
.observeOn(AndroidSchedulers.mainThread())
Expand Down Expand Up @@ -743,6 +747,22 @@ && isPlaybackResumeEnabled(this)
NavigationHelper.sendPlayerStartedEvent(context);
}

/**
* Open fullscreen on tablets where the option to have the main player start automatically in
* fullscreen mode is on. Rotating the device to landscape is already done in {@link
* VideoDetailFragment#openVideoPlayer(boolean)} when the thumbnail is clicked, and that's
* enough for phones, but not for tablets since the mini player can be also shown in landscape.
*/
private void directlyOpenFullscreenIfNeeded() {
if (fragmentListener != null
&& PlayerHelper.isStartMainPlayerFullscreenEnabled(service)
&& DeviceUtils.isTablet(service)
&& videoPlayerSelected()
&& PlayerHelper.globalScreenOrientationLocked(service)) {
Stypox marked this conversation as resolved.
Show resolved Hide resolved
fragmentListener.onScreenRotationButtonClicked();
}
}

private void initPlayback(@NonNull final PlayQueue queue,
@RepeatMode final int repeatMode,
final float playbackSpeed,
Expand Down Expand Up @@ -3855,11 +3875,9 @@ public void toggleFullscreen() {
if (DEBUG) {
Log.d(TAG, "toggleFullscreen() called");
}
if (popupPlayerSelected() || exoPlayerIsNull() || currentMetadata == null
|| fragmentListener == null) {
if (popupPlayerSelected() || exoPlayerIsNull() || fragmentListener == null) {
return;
}
//changeState(STATE_BLOCKED); TODO check what this does

isFullscreen = !isFullscreen;
if (!isFullscreen) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ public static boolean isBrightnessGestureEnabled(@NonNull final Context context)
.getBoolean(context.getString(R.string.brightness_gesture_control_key), true);
}

public static boolean isStartMainPlayerFullscreenEnabled(@NonNull final Context context) {
return getPreferences(context)
.getBoolean(context.getString(R.string.start_main_player_fullscreen_key), false);
}

public static boolean isAutoQueueEnabled(@NonNull final Context context) {
return getPreferences(context)
.getBoolean(context.getString(R.string.auto_queue_key), false);
Expand Down
Loading