mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-11 19:29:25 +01:00
Merge pull request #5459 from Stypox/fullscreen-autoplay
Add option to directly open fullscreen when the main player starts
This commit is contained in:
commit
81fa0c1558
@ -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
|
||||
@ -220,7 +220,7 @@ public final class VideoDetailFragment
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLandscape()) {
|
||||
if (DeviceUtils.isLandscape(requireContext())) {
|
||||
// If the video is playing but orientation changed
|
||||
// let's make the video in fullscreen again
|
||||
checkLandscape();
|
||||
@ -241,7 +241,7 @@ public final class VideoDetailFragment
|
||||
&& isAutoplayEnabled()
|
||||
&& player.getParentActivity() == null)) {
|
||||
autoPlayEnabled = true; // forcefully start playing
|
||||
openVideoPlayer();
|
||||
openVideoPlayerAutoFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,7 +499,7 @@ public final class VideoDetailFragment
|
||||
break;
|
||||
case R.id.detail_thumbnail_root_layout:
|
||||
autoPlayEnabled = true; // forcefully start playing
|
||||
openVideoPlayer();
|
||||
openVideoPlayerAutoFullscreen();
|
||||
break;
|
||||
case R.id.detail_title_root_layout:
|
||||
toggleTitleAndSecondaryControls();
|
||||
@ -516,7 +516,7 @@ public final class VideoDetailFragment
|
||||
showSystemUi();
|
||||
} else {
|
||||
autoPlayEnabled = true; // forcefully start playing
|
||||
openVideoPlayer();
|
||||
openVideoPlayer(false);
|
||||
}
|
||||
|
||||
setOverlayPlayPauseImage(isPlayerAvailable() && player.isPlaying());
|
||||
@ -762,7 +762,7 @@ public final class VideoDetailFragment
|
||||
|
||||
private void setupFromHistoryItem(final StackItem item) {
|
||||
setAutoPlay(false);
|
||||
hideMainPlayer();
|
||||
hideMainPlayerOnLoadingNewStream();
|
||||
|
||||
setInitialData(item.getServiceId(), item.getUrl(),
|
||||
item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue());
|
||||
@ -882,7 +882,7 @@ public final class VideoDetailFragment
|
||||
.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();
|
||||
@ -897,8 +897,9 @@ public final class VideoDetailFragment
|
||||
stack.push(new StackItem(serviceId, url, title, playQueue));
|
||||
}
|
||||
}
|
||||
|
||||
if (isAutoplayEnabled()) {
|
||||
openVideoPlayer();
|
||||
openVideoPlayerAutoFullscreen();
|
||||
}
|
||||
}
|
||||
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||
@ -1103,7 +1104,29 @@ public final class VideoDetailFragment
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@ -1112,6 +1135,18 @@ public final class VideoDetailFragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
@ -1145,12 +1180,19 @@ public final class VideoDetailFragment
|
||||
}
|
||||
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()) {
|
||||
@ -1158,8 +1200,12 @@ public final class VideoDetailFragment
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -1252,7 +1298,7 @@ public final class VideoDetailFragment
|
||||
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);
|
||||
@ -1275,7 +1321,7 @@ public final class VideoDetailFragment
|
||||
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
|
||||
@ -1808,7 +1854,7 @@ public final class VideoDetailFragment
|
||||
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
|
||||
// Properly exit from fullscreen
|
||||
toggleFullscreenIfInFullscreenMode();
|
||||
hideMainPlayer();
|
||||
hideMainPlayerOnLoadingNewStream();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1864,13 +1910,14 @@ public final class VideoDetailFragment
|
||||
// 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;
|
||||
|
||||
@ -1942,15 +1989,17 @@ public final class VideoDetailFragment
|
||||
| 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);
|
||||
}
|
||||
@ -2022,15 +2071,6 @@ public final class VideoDetailFragment
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
@ -2213,7 +2253,7 @@ public final class VideoDetailFragment
|
||||
setOverlayElementsClickable(false);
|
||||
hideSystemUiIfNeeded();
|
||||
// Conditions when the player should be expanded to fullscreen
|
||||
if (isLandscape()
|
||||
if (DeviceUtils.isLandscape(requireContext())
|
||||
&& isPlayerAvailable()
|
||||
&& player.isPlaying()
|
||||
&& !player.isFullscreen()
|
||||
|
@ -24,7 +24,6 @@ import android.content.Context;
|
||||
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;
|
||||
@ -36,6 +35,7 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
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;
|
||||
@ -133,32 +133,29 @@ public final class MainPlayer extends Service {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,11 +219,8 @@ public final class MainPlayer extends Service {
|
||||
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
|
||||
|
@ -621,6 +621,9 @@ public final class Player implements
|
||||
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;
|
||||
@ -672,6 +675,7 @@ public final class Player implements
|
||||
&& isPlaybackResumeEnabled(this)
|
||||
&& !samePlayQueue
|
||||
&& !newQueue.isEmpty()
|
||||
&& newQueue.getItem() != null
|
||||
&& newQueue.getItem().getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET) {
|
||||
databaseUpdateDisposable.add(recordManager.loadStreamState(newQueue.getItem())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@ -743,6 +747,22 @@ public final class Player implements
|
||||
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)) {
|
||||
fragmentListener.onScreenRotationButtonClicked();
|
||||
}
|
||||
}
|
||||
|
||||
private void initPlayback(@NonNull final PlayQueue queue,
|
||||
@RepeatMode final int repeatMode,
|
||||
final float playbackSpeed,
|
||||
@ -3855,11 +3875,9 @@ public final class Player implements
|
||||
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) {
|
||||
|
@ -239,6 +239,11 @@ public final class PlayerHelper {
|
||||
.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);
|
||||
|
@ -11,6 +11,7 @@ import android.view.KeyEvent;
|
||||
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
@ -130,4 +131,13 @@ public final class DeviceUtils {
|
||||
&& !HI3798MV200
|
||||
&& !CVT_MT5886_EU_1G;
|
||||
}
|
||||
|
||||
public static boolean isLandscape(final Context context) {
|
||||
return context.getResources().getDisplayMetrics().heightPixels < context.getResources()
|
||||
.getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public static boolean isInMultiWindow(final AppCompatActivity activity) {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode();
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +366,9 @@ public final class NavigationHelper {
|
||||
if (switchingPlayers) {
|
||||
// Situation when user switches from players to main player. All needed data is
|
||||
// here, we can start watching (assuming newQueue equals playQueue).
|
||||
detailFragment.openVideoPlayer();
|
||||
// Starting directly in fullscreen if the previous player type was popup.
|
||||
detailFragment.openVideoPlayer(playerType == MainPlayer.PlayerType.POPUP
|
||||
|| PlayerHelper.isStartMainPlayerFullscreenEnabled(context));
|
||||
} else {
|
||||
detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue);
|
||||
}
|
||||
|
@ -73,6 +73,8 @@
|
||||
<item>@string/minimize_on_exit_popup_description</item>
|
||||
</string-array>
|
||||
|
||||
<string name="start_main_player_fullscreen_key" translatable="false">start_main_player_fullscreen_key</string>
|
||||
|
||||
<string name="autoplay_key" translatable="false">autoplay_key</string>
|
||||
<string name="autoplay_value" translatable="false">@string/autoplay_wifi_key</string>
|
||||
<string name="autoplay_always_key" translatable="false">autoplay_always_key</string>
|
||||
|
@ -132,6 +132,8 @@
|
||||
<string name="resume_on_audio_focus_gain_title">Resume playing</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">Continue playing after interruptions (e.g. phonecalls)</string>
|
||||
<string name="download_dialog_title">Download</string>
|
||||
<string name="start_main_player_fullscreen_title">Start main player in fullscreen</string>
|
||||
<string name="start_main_player_fullscreen_summary">Do not start videos in the mini player, but turn to fullscreen mode directly, if auto rotation is locked. You can still access the mini player by exiting fullscreen.</string>
|
||||
<string name="autoplay_title">Autoplay</string>
|
||||
<string name="show_hold_to_append_title">Show \"Hold to append\" tip</string>
|
||||
<string name="show_hold_to_append_summary">Show tip when pressing the background or the popup button in video \"Details:\"</string>
|
||||
|
@ -129,6 +129,14 @@
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="@string/start_main_player_fullscreen_key"
|
||||
android:summary="@string/start_main_player_fullscreen_summary"
|
||||
android:title="@string/start_main_player_fullscreen_title"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/autoplay_value"
|
||||
android:entries="@array/autoplay_type_description"
|
||||
|
Loading…
Reference in New Issue
Block a user