diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 49e72328e..8d5978c5f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -42,8 +42,6 @@ import static org.schabi.newpipe.player.notification.NotificationConstants.ACTIO import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT; import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE; -import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; -import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -116,7 +114,6 @@ import org.schabi.newpipe.player.ui.PlayerUiList; import org.schabi.newpipe.player.ui.PopupPlayerUi; import org.schabi.newpipe.player.ui.VideoPlayerUi; import org.schabi.newpipe.util.DependentPreferenceHelper; -import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.SerializedCache; @@ -292,7 +289,7 @@ public final class Player implements PlaybackListener, Listener { context.getString( R.string.use_exoplayer_decoder_fallback_key), false)); - videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver()); + videoResolver = new VideoPlaybackResolver(context, dataSource); audioResolver = new AudioPlaybackResolver(context, dataSource); currentThumbnailTarget = getCurrentThumbnailTarget(); @@ -306,25 +303,6 @@ public final class Player implements PlaybackListener, Listener { new NotificationPlayerUi(this) ); } - - private VideoPlaybackResolver.QualityResolver getQualityResolver() { - return new VideoPlaybackResolver.QualityResolver() { - @Override - public int getDefaultResolutionIndex(final List sortedVideos) { - return videoPlayerSelected() - ? ListHelper.getDefaultResolutionIndex(context, sortedVideos) - : ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos); - } - - @Override - public int getOverrideResolutionIndex(final List sortedVideos, - final String playbackQuality) { - return videoPlayerSelected() - ? getResolutionIndex(context, sortedVideos, playbackQuality) - : getPopupResolutionIndex(context, sortedVideos, playbackQuality); - } - }; - } //endregion @@ -1908,7 +1886,10 @@ public final class Player implements PlaybackListener, Listener { // Note that the video is not fetched when the app is in background because the video // renderer is fully disabled (see useVideoSource method), except for HLS streams // (see https://github.com/google/ExoPlayer/issues/9282). - return videoResolver.resolve(info); + return videoResolver.resolve(info, videoPlayerSelected() + ? VideoPlaybackResolver.SelectedPlayer.MAIN + : VideoPlaybackResolver.SelectedPlayer.POPUP + ); } public void disablePreloadingOfCurrentTrack() { diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index 2d4404b2a..9c0dc0edb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -45,7 +45,6 @@ public class AudioPlaybackResolver implements PlaybackResolver { * @param info of the stream * @return the audio source to use or null if none could be found */ - @Override @Nullable public MediaSource resolve(@NonNull final StreamInfo info) { final MediaSource liveSource = PlaybackResolver.maybeBuildLiveMediaSource(dataSource, info); diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java index e204b8372..de0d5be73 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java @@ -46,11 +46,10 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; /** - * This interface is just a shorthand for {@link Resolver} with {@link StreamInfo} as source and - * {@link MediaSource} as product. It contains many static methods that can be used by classes + * This interface contains many static methods that can be used by classes * implementing this interface, and nothing else. */ -public interface PlaybackResolver extends Resolver { +public interface PlaybackResolver { String TAG = PlaybackResolver.class.getSimpleName(); diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java deleted file mode 100644 index a3e1db5b4..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.schabi.newpipe.player.resolver; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public interface Resolver { - @Nullable - Product resolve(@NonNull Source source); -} diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index 670c13934..14c1f9f1e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -39,8 +39,6 @@ public class VideoPlaybackResolver implements PlaybackResolver { private final Context context; @NonNull private final PlayerDataSource dataSource; - @NonNull - private final QualityResolver qualityResolver; private SourceType streamSourceType; @Nullable @@ -54,17 +52,23 @@ public class VideoPlaybackResolver implements PlaybackResolver { VIDEO_WITH_AUDIO_OR_AUDIO_ONLY } - public VideoPlaybackResolver(@NonNull final Context context, - @NonNull final PlayerDataSource dataSource, - @NonNull final QualityResolver qualityResolver) { - this.context = context; - this.dataSource = dataSource; - this.qualityResolver = qualityResolver; + /** + * Depending on the player we select different video streams. + */ + public enum SelectedPlayer { + MAIN, + POPUP + } + + public VideoPlaybackResolver(@NonNull final Context context, + @NonNull final PlayerDataSource dataSource) { + this.context = context; + this.dataSource = dataSource; } - @Override @Nullable - public MediaSource resolve(@NonNull final StreamInfo info) { + public MediaSource resolve(@NonNull final StreamInfo info, + @NonNull final SelectedPlayer selectedPlayer) { final MediaSource liveSource = PlaybackResolver.maybeBuildLiveMediaSource(dataSource, info); if (liveSource != null) { streamSourceType = SourceType.LIVE_STREAM; @@ -80,14 +84,30 @@ public class VideoPlaybackResolver implements PlaybackResolver { final List audioStreamsList = getFilteredAudioStreams(context, info.getAudioStreams()); - final int videoIndex; + int videoIndex = -999; if (videoStreamsList.isEmpty()) { videoIndex = -1; } else if (playbackQuality == null) { - videoIndex = qualityResolver.getDefaultResolutionIndex(videoStreamsList); + switch (selectedPlayer) { + case MAIN -> { + videoIndex = ListHelper.getDefaultResolutionIndex( + context, + videoStreamsList + ); + } + case POPUP -> { + videoIndex = ListHelper.getPopupDefaultResolutionIndex( + context, + videoStreamsList + ); + } + } } else { - videoIndex = qualityResolver.getOverrideResolutionIndex(videoStreamsList, - getPlaybackQuality()); + videoIndex = ListHelper.getDefaultResolutionWithDefaultFormat( + context, + getPlaybackQuality(), + videoStreamsList + ); } final int audioIndex = @@ -195,10 +215,4 @@ public class VideoPlaybackResolver implements PlaybackResolver { public void setAudioTrack(@Nullable final String audioLanguage) { this.audioTrack = audioLanguage; } - - public interface QualityResolver { - int getDefaultResolutionIndex(List sortedVideos); - - int getOverrideResolutionIndex(List sortedVideos, String playbackQuality); - } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index f1904565d..2bd4a6d86 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -78,23 +78,11 @@ public final class ListHelper { */ public static int getDefaultResolutionIndex(final Context context, final List videoStreams) { - final String defaultResolution = computeDefaultResolution(context, + final String defaultResolution = getPreferredResolutionOrCurrentLimit(context, R.string.default_resolution_key, R.string.default_resolution_value); return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams); } - /** - * @param context Android app context - * @param videoStreams list of the video streams to check - * @param defaultResolution the default resolution to look for - * @return index of the video stream with the default index - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) - */ - public static int getResolutionIndex(final Context context, - final List videoStreams, - final String defaultResolution) { - return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams); - } /** * @param context Android app context @@ -104,24 +92,11 @@ public final class ListHelper { */ public static int getPopupDefaultResolutionIndex(final Context context, final List videoStreams) { - final String defaultResolution = computeDefaultResolution(context, + final String defaultResolution = getPreferredResolutionOrCurrentLimit(context, R.string.default_popup_resolution_key, R.string.default_popup_resolution_value); return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams); } - /** - * @param context Android app context - * @param videoStreams list of the video streams to check - * @param defaultResolution the default resolution to look for - * @return index of the video stream with the default index - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) - */ - public static int getPopupResolutionIndex(final Context context, - final List videoStreams, - final String defaultResolution) { - return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams); - } - public static int getDefaultAudioFormat(final Context context, final List audioStreams) { return getAudioIndexByHighestRank(audioStreams, @@ -390,23 +365,42 @@ public final class ListHelper { .collect(Collectors.toList()); } - private static String computeDefaultResolution(@NonNull final Context context, final int key, - final int value) { + /** Lookup the preferred resolution and the current resolution limit. + * + * @param context App context + * @param defaultResolutionKey The defaultResolution preference key + * @param defaultResolutionDefaultValue Default resolution if key does not exist + * @return The smaller resolution of either the preference or the current limit. + */ + private static String getPreferredResolutionOrCurrentLimit( + @NonNull final Context context, + final int defaultResolutionKey, + final int defaultResolutionDefaultValue + ) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); // Load the preferred resolution otherwise the best available - String resolution = preferences != null - ? preferences.getString(context.getString(key), context.getString(value)) - : context.getString(R.string.best_resolution_key); + final String preferredResolution = preferences.getString( + context.getString(defaultResolutionKey), + context.getString(defaultResolutionDefaultValue) + ); - final String maxResolution = getResolutionLimit(context); - if (maxResolution != null - && (resolution.equals(context.getString(R.string.best_resolution_key)) - || compareVideoStreamResolution(maxResolution, resolution) < 1)) { - resolution = maxResolution; + // clamp to the currently maximum allowed resolution + final String result; + final String resolutionLimit = getCurrentResolutionLimit(context); + if (resolutionLimit != null + && ( + // if the preference is best_resolution + preferredResolution.equals(context.getString(R.string.best_resolution_key)) + // or the preference is higher than the current max allowed resolution + || compareVideoStreamResolution(resolutionLimit, preferredResolution) < 1 + )) { + result = resolutionLimit; + } else { + result = preferredResolution; } - return resolution; + return result; } /** @@ -627,14 +621,14 @@ public final class ListHelper { /** * Fetches the desired resolution or returns the default if it is not found. - * The resolution will be reduced if video chocking is active. + * The resolution will be reduced if video choking is active. * * @param context Android app context * @param defaultResolution the default resolution * @param videoStreams the list of video streams to check * @return the index of the preferred video stream */ - private static int getDefaultResolutionWithDefaultFormat(@NonNull final Context context, + public static int getDefaultResolutionWithDefaultFormat(@NonNull final Context context, final String defaultResolution, final List videoStreams) { final MediaFormat defaultFormat = getDefaultFormat(context, @@ -677,6 +671,14 @@ public final class ListHelper { return format; } + /** #Comparator for two resolution strings. + * + * See {@link #sortStreamList} for ordering. + * + * @param r1 first + * @param r2 second + * @return comparison int + */ private static int compareVideoStreamResolution(@NonNull final String r1, @NonNull final String r2) { try { @@ -693,31 +695,37 @@ public final class ListHelper { } } - static boolean isLimitingDataUsage(@NonNull final Context context) { - return getResolutionLimit(context) != null; + /** Does the application have a maximum resolution set? + * + * @param context App context + * @return whether a max resolution is set + */ + static boolean isCurrentlyLimitingDataUsage(@NonNull final Context context) { + return getCurrentResolutionLimit(context) != null; } /** - * The maximum resolution allowed. + * The maximum current resolution allowed by application settings. + * Takes into account whether we are on a metered network. * * @param context App context - * @return maximum resolution allowed or null if there is no maximum + * @return current maximum resolution allowed or null if there is no maximum */ - private static String getResolutionLimit(@NonNull final Context context) { - String resolutionLimit = null; + private static String getCurrentResolutionLimit(@NonNull final Context context) { + String currentResolutionLimit = null; if (isMeteredNetwork(context)) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); final String defValue = context.getString(R.string.limit_data_usage_none_key); final String value = preferences.getString( context.getString(R.string.limit_mobile_data_usage_key), defValue); - resolutionLimit = defValue.equals(value) ? null : value; + currentResolutionLimit = defValue.equals(value) ? null : value; } - return resolutionLimit; + return currentResolutionLimit; } /** - * The current network is metered (like mobile data)? + * Is the current network metered (like mobile data)? * * @param context App context * @return {@code true} if connected to a metered network @@ -744,7 +752,7 @@ public final class ListHelper { final @NonNull Context context) { final MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_audio_format_key, R.string.default_audio_format_value); - return getAudioFormatComparator(defaultFormat, isLimitingDataUsage(context)); + return getAudioFormatComparator(defaultFormat, isCurrentlyLimitingDataUsage(context)); } /** diff --git a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java index 75d9a3892..c3677055e 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java @@ -31,7 +31,7 @@ public class SecondaryStreamHelper { * Finds an audio stream compatible with the provided video-only stream, so that the two streams * can be combined in a single file by the downloader. If there are multiple available audio * streams, chooses either the highest or the lowest quality one based on - * {@link ListHelper#isLimitingDataUsage(Context)}. + * {@link ListHelper#isCurrentlyLimitingDataUsage(Context)}. * * @param context Android context * @param audioStreams list of audio streams @@ -56,7 +56,7 @@ public class SecondaryStreamHelper { } final boolean m4v = mediaFormat == MediaFormat.MPEG_4; - final boolean isLimitingDataUsage = ListHelper.isLimitingDataUsage(context); + final boolean isLimitingDataUsage = ListHelper.isCurrentlyLimitingDataUsage(context); Comparator comparator = ListHelper.getAudioFormatComparator( m4v ? MediaFormat.M4A : MediaFormat.WEBMA, isLimitingDataUsage);