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

Selectable behavior of Back button #6895

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,28 @@ public boolean onBackPressed() {
Log.d(TAG, "onBackPressed() called");
}

// User can choose a behavior of the back button.
// We go down and check if it's OK to stop at some point based on the user's
// preference.
// For example, if the user always wants to minimize into the mini player
// when the backstack is empty, we have to ensure that:
// - nothing is in the backstack
// - nothing is in the PlayQueue's history
// - the player is in non-fullscreen mode.
// Otherwise it takes priority and the minimize action will not happen right now
final int behavior = PlayerHelper.getBackBehavior(activity);

if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE_FULLSCREEN) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE_FULLSCREEN) {
// if the user chose BACK_BEHAVIOR_CLOSE_FULLSCREEN, we always close the (video) player, independently of whether it is fullscreen or not
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE_FULLSCREEN) {

if (!isPlayerAvailable() || player.videoPlayerSelected()) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
requireView().postDelayed(this::rotateScreenOnPhoneIfInLandscape, 500);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks suspicious, how did you calculate the 500ms delay to make sure the rotation happens correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, forgot to made something about that. This delay was just brute-forced. If it will not work for someone I'll try to find a better way.

} else {
// Since it's not a video playing we can't close the player
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
return true;
}

// If we are in fullscreen mode just exit from it via first back press
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(obv break it in multiple lines)

Suggested change
// If we are in fullscreen mode just exit from it via first back press
// If we are in fullscreen, and the user does not want to completely close the player when pressing back while in fullscreen (otherwise the option above would have kicked in), exit from fullscreen via the first back press

if (isPlayerAvailable() && player.isFullscreen()) {
if (!DeviceUtils.isTablet(activity)) {
Expand All @@ -765,6 +787,21 @@ public boolean onBackPressed() {
return true;
}

if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE) {
// just like BACK_BEHAVIOR_CLOSE_FULLSCREEN, but the fullscreen case is handled just above here
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE) {

if (!isPlayerAvailable() || player.videoPlayerSelected()) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
} else {
// Since it's not a video playing we can't close the player
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
return true;
}

if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE) {
// just collapse the video detail fragment, whatever the player's state (but the fullscreen case is handled above)
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE) {

bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
return true;
}

// If we have something in history of played items we replay it here
if (isPlayerAvailable()
&& player.getPlayQueue() != null
Expand All @@ -773,10 +810,30 @@ public boolean onBackPressed() {
return true; // no code here, as previous() was used in the if
}

// That means that we are on the start of the stack,
// If we passed previous check at the top `player.getPlayQueue().previous()`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If we passed previous check at the top `player.getPlayQueue().previous()`
// If we passed the previous check at the top, that is, `player.getPlayQueue().previous()` returned true,

// then we can be sure that queue have no more streams in the history.
// If the user chose BACK_BEHAVIOR_PREV_PLAYLIST behavior
// we have to minimize the player into the mini player here
if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_PREV_PLAYLIST) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
return true;
Comment on lines +818 to +819
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still do not understand why this code would cause the previous item from the playlist to be played. Also, how do you check if a play queue item belongs to a playlist, and if its previous item also belongs to a playlist? And where is the code that queues the previous item from the playlist?

Or maybe I didn't understand what you mean by playlist, do you mean play queue (though still the code would not explain that, either)?

Copy link
Contributor Author

@avently avently Feb 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I mean PlayQueue that has a history of played items.
See https://github.com/TeamNewPipe/NewPipe/blob/dev/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java#L45

It's a List that holds previously played items from this queue.

Here https://github.com/TeamNewPipe/NewPipe/blob/dev/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java#L772
I call this method https://github.com/TeamNewPipe/NewPipe/blob/dev/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java#L506
which returns true in case it started to play previously played stream. If it isn't then it returns false and the body of if at https://github.com/TeamNewPipe/NewPipe/blob/dev/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java#L772 will be skipped.

Maybe would be better to rename previous() method to something like playPreviousFromHistoryIfAny() or something like that. Or maybe to make two methods instead of previous() like hasPrevious() and playPrevious(). But I think having one method is better because calling playPrevious without checking hasPrevious first (when no previous stream) would raise an exception which is not needed at all.

}
avently marked this conversation as resolved.
Show resolved Hide resolved

// That means that we are on the start of the stack
if (stack.size() <= 1) {
restoreDefaultOrientation();
return false; // let MainActivity handle the onBack (e.g. to minimize the mini player)

if (behavior == PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE_OR_CLOSE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is "BACK_BEHAVIOR_HIDE_OR_CLOSE" here and not just after "BACK_BEHAVIOR_HIDE"? Shouldn't the two act the same way, with the only difference that "BACK_BEHAVIOR_HIDE_OR_CLOSE" should close the player if nothing is playing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to have a backstack working (like in BACK_BEHAVIOR_PREV_ANY) but to allow to close mini player completely. With BACK_BEHAVIOR_PREV_ANY the mini player will be at the bottom even when video is paused. With this option you're able to hide it in this case.

That's why it is the last check and it is inside f (stack.size() <= 1) {, which guarantees that backstack is empty and nothing can be played.

if (!isPlayerAvailable() || player.getPlayQueue() == null
|| (player.videoPlayerSelected()
&& !player.getPlayQueue().equals(playQueue))) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
return true;
}
}

// let MainActivity handle the onBack (e.g. to minimize the mini player)
return false;
}

// Remove top
Expand Down Expand Up @@ -1092,6 +1149,15 @@ private void toggleFullscreenIfInFullscreenMode() {
}
}

private void rotateScreenOnPhoneIfInLandscape() {
if (!DeviceUtils.isTablet(activity)
&& !DeviceUtils.isTv(activity)
&& DeviceUtils.isLandscape(requireContext())
&& globalScreenOrientationLocked(activity)) {
onScreenRotationButtonClicked();
}
}

private void openBackgroundPlayer(final boolean append) {
final AudioStream audioStream = currentInfo.getAudioStreams()
.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
Expand Down Expand Up @@ -2180,6 +2246,10 @@ private void showExternalPlaybackDialog() {
* Remove unneeded information while waiting for a next task
* */
private void cleanUp() {
// On every clean up operation we want to be in non-fullscreen mode,
// since it returns UI into it's initial look when nothing is played
toggleFullscreenIfInFullscreenMode();
avently marked this conversation as resolved.
Show resolved Hide resolved

// New beginning
stack.clear();
if (currentWorker != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS;
import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER;
import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_PREV_ANY;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_PREV_PLAYLIST;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_HIDE_OR_CLOSE;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE;
import static org.schabi.newpipe.player.helper.PlayerHelper.BackBehavior.BACK_BEHAVIOR_CLOSE_FULLSCREEN;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE;
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
Expand Down Expand Up @@ -95,6 +101,19 @@ public final class PlayerHelper {
int MINIMIZE_ON_EXIT_MODE_POPUP = 2;
}

@Retention(SOURCE)
@IntDef({BACK_BEHAVIOR_PREV_ANY, BACK_BEHAVIOR_PREV_PLAYLIST,
BACK_BEHAVIOR_HIDE, BACK_BEHAVIOR_HIDE_OR_CLOSE,
BACK_BEHAVIOR_CLOSE, BACK_BEHAVIOR_CLOSE_FULLSCREEN})
public @interface BackBehavior {
int BACK_BEHAVIOR_PREV_ANY = 0;
int BACK_BEHAVIOR_PREV_PLAYLIST = 1;
int BACK_BEHAVIOR_HIDE = 2;
int BACK_BEHAVIOR_HIDE_OR_CLOSE = 3;
int BACK_BEHAVIOR_CLOSE = 4;
int BACK_BEHAVIOR_CLOSE_FULLSCREEN = 5;
}
Comment on lines +108 to +115
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't name all values inside "BackBehavior" with the prefix "BACK_BEHAVIOR_" because it's redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALL interfaces in PlayerHelper are prefixed with their name. It's just made the same as the rest of the code


private PlayerHelper() { }

////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -267,6 +286,26 @@ public static int getMinimizeOnExitAction(@NonNull final Context context) {
}
}

@BackBehavior
public static int getBackBehavior(@NonNull final Context context) {
final String action = getPreferences(context)
.getString(context.getString(R.string.back_behavior_key),
context.getString(R.string.back_behavior_value));
if (action.equals(context.getString(R.string.back_behavior_prev_any_key))) {
return BACK_BEHAVIOR_PREV_ANY; // default
} else if (action.equals(context.getString(R.string.back_behavior_prev_playlist_key))) {
return BACK_BEHAVIOR_PREV_PLAYLIST;
} else if (action.equals(context.getString(R.string.back_behavior_hide_key))) {
return BACK_BEHAVIOR_HIDE;
} else if (action.equals(context.getString(R.string.back_behavior_hide_or_close_key))) {
return BACK_BEHAVIOR_HIDE_OR_CLOSE;
} else if (action.equals(context.getString(R.string.back_behavior_close_key))) {
return BACK_BEHAVIOR_CLOSE;
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This else is useless and can be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With or without else branch I have to write return BACK_BEHAVIOR_CLOSE_FULLSCREEN anyway. By using else the code looks better than:

...
else if(....)

return BACK_BEHAVIOR_CLOSE_FULLSCREEN

return BACK_BEHAVIOR_CLOSE_FULLSCREEN;
}
}

@AutoplayType
public static int getAutoplayType(@NonNull final Context context) {
final String type = getPreferences(context).getString(
Expand Down
25 changes: 25 additions & 0 deletions app/src/main/res/values/settings_keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,31 @@
<item>@string/never</item>
</string-array>

<string name="back_behavior_key" translatable="false">back_behavior_key</string>
<string name="back_behavior_value" translatable="false">@string/back_behavior_prev_any_key</string>
<string name="back_behavior_prev_any_key" translatable="false">back_behavior_prev_any_key</string>
<string name="back_behavior_prev_playlist_key" translatable="false">back_behavior_prev_playlist_key</string>
<string name="back_behavior_hide_key" translatable="false">back_behavior_hide_key</string>
<string name="back_behavior_hide_or_close_key" translatable="false">back_behavior_hide_or_close_key</string>
<string name="back_behavior_close_key" translatable="false">back_behavior_close_key</string>
<string name="back_behavior_close_fullscreen_key" translatable="false">back_behavior_close_fullscreen_key</string>
<string-array name="back_behavior_action_key" translatable="false">
<item>@string/back_behavior_prev_any_key</item>
<item>@string/back_behavior_prev_playlist_key</item>
<item>@string/back_behavior_hide_key</item>
<item>@string/back_behavior_hide_or_close_key</item>
<item>@string/back_behavior_close_key</item>
<item>@string/back_behavior_close_fullscreen_key</item>
</string-array>
<string-array name="back_behavior_action_description" translatable="false">
<item>@string/back_behavior_prev_any_description</item>
<item>@string/back_behavior_prev_playlist_description</item>
<item>@string/back_behavior_hide_description</item>
<item>@string/back_behavior_hide_or_close_description</item>
<item>@string/back_behavior_close_description</item>
<item>@string/back_behavior_close_fullscreen_description</item>
</string-array>

<string name="seekbar_preview_thumbnail_key">seekbar_preview_thumbnail_key</string>
<string name="seekbar_preview_thumbnail_high_quality">seekbar_preview_thumbnail_high_quality</string>
<string name="seekbar_preview_thumbnail_low_quality">seekbar_preview_thumbnail_low_quality</string>
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,15 @@
<string name="minimize_on_exit_none_description">None</string>
<string name="minimize_on_exit_background_description">Minimize to background player</string>
<string name="minimize_on_exit_popup_description">Minimize to popup player</string>
<!-- Back button behaviour in a fragment -->
<string name="back_behavior_title">Back behavior in the player</string>
<string name="back_behavior_summary">What to do when the back button is pressed while inside a player — %s</string>
<string name="back_behavior_prev_any_description">Open previous video from backstack of opened videos</string>
<string name="back_behavior_prev_playlist_description">Open previous video only if belonging to a playlist</string>
<string name="back_behavior_hide_description">Minimize into the mini player</string>
<string name="back_behavior_hide_or_close_description">Minimize into the mini player if something is playing, close otherwise</string>
<string name="back_behavior_close_description">Close the video player</string>
<string name="back_behavior_close_fullscreen_description">Close the video player even in fullscreen</string>
<!-- Autoplay behavior -->
<string name="autoplay_summary">Start playback automatically — %s</string>
<string name="wifi_only">Only on Wi-Fi</string>
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/xml/video_audio_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@
app:singleLineTitle="false"
app:iconSpaceReserved="false" />

<ListPreference
android:defaultValue="@string/back_behavior_value"
android:entries="@array/back_behavior_action_description"
android:entryValues="@array/back_behavior_action_key"
android:key="@string/back_behavior_key"
android:summary="@string/back_behavior_summary"
android:title="@string/back_behavior_title"
app:iconSpaceReserved="false" />

<ListPreference
android:defaultValue="@string/autoplay_value"
android:entries="@array/autoplay_type_description"
Expand Down