diff --git a/app/build.gradle b/app/build.gradle index 421e7d3b95..06f3a6b5c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,8 @@ android { defaultConfig { applicationId "InfinityLoop1309.NewPipeEnhanced" - minSdk 21 + // media3 1.9+ requires API 23 (media3-session). Bumped from 21 -> drops Android 5.0/5.1. + minSdk 23 //noinspection ExpiredTargetSdkVersion targetSdk 33 versionCode 1100 @@ -137,7 +138,7 @@ ext { androidxRoomVersion = '2.4.2' androidxWorkVersion = '2.10.2' - exoPlayerVersion = '2.18.7' + media3Version = '1.10.1' googleAutoServiceVersion = '1.0.1' groupieVersion = '2.10.0' markwonVersion = '4.6.2' @@ -271,8 +272,14 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:3.12.13" // Media player - implementation "com.google.android.exoplayer:exoplayer:${exoPlayerVersion}" - implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerVersion}" + implementation "androidx.media3:media3-exoplayer:${media3Version}" + implementation "androidx.media3:media3-exoplayer-dash:${media3Version}" + implementation "androidx.media3:media3-exoplayer-hls:${media3Version}" + implementation "androidx.media3:media3-exoplayer-smoothstreaming:${media3Version}" + implementation "androidx.media3:media3-datasource:${media3Version}" + implementation "androidx.media3:media3-ui:${media3Version}" + implementation "androidx.media3:media3-session:${media3Version}" + implementation "androidx.media3:media3-common:${media3Version}" // Metadata generator for service descriptors compileOnly "com.google.auto.service:auto-service-annotations:${googleAutoServiceVersion}" diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt index aa849655a9..a34ae5ed22 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -2,7 +2,7 @@ package org.schabi.newpipe.error import android.os.Parcelable import androidx.annotation.StringRes -import com.google.android.exoplayer2.ExoPlaybackException +import androidx.media3.exoplayer.ExoPlaybackException import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.schabi.newpipe.R diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 63835ed6e7..c86d04ecf0 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -46,8 +46,8 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.tabs.TabLayout; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java index ae704e88c9..af6c41c2be 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java @@ -13,9 +13,9 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.PlaybackException; +import androidx.media3.common.C; +import androidx.media3.exoplayer.ExoPlaybackException; +import androidx.media3.common.PlaybackException; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ListRadioIconItemBinding; @@ -29,9 +29,9 @@ import java.util.Map; import java.util.function.Supplier; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_DECODING_FAILED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_UNSPECIFIED; +import static androidx.media3.common.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW; +import static androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FAILED; +import static androidx.media3.common.PlaybackException.ERROR_CODE_UNSPECIFIED; /** * Outsourced logic for crashing the player in the {@link VideoDetailFragment}. diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 205b786408..c06540db9d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -26,8 +26,8 @@ import java.util.List; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; +import static androidx.media3.common.Player.REPEAT_MODE_ALL; +import static androidx.media3.common.Player.REPEAT_MODE_ONE; import static org.schabi.newpipe.player.PlayerService.ACTION_CLOSE; import static org.schabi.newpipe.player.PlayerService.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.PlayerService.ACTION_FAST_REWIND; @@ -269,19 +269,19 @@ private NotificationCompat.Action getAction( case NotificationConstants.SMART_REWIND_PREVIOUS: if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { - return getAction(player, R.drawable.exo_notification_previous, + return getAction(player, R.drawable.exo_icon_previous, R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS); } else { - return getAction(player, R.drawable.exo_controls_rewind, + return getAction(player, R.drawable.exo_icon_rewind, R.string.exo_controls_rewind_description, ACTION_FAST_REWIND); } case NotificationConstants.SMART_FORWARD_NEXT: if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { - return getAction(player, R.drawable.exo_notification_next, + return getAction(player, R.drawable.exo_icon_next, R.string.exo_controls_next_description, ACTION_PLAY_NEXT); } else { - return getAction(player, R.drawable.exo_controls_fastforward, + return getAction(player, R.drawable.exo_icon_fastforward, R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); } @@ -303,10 +303,10 @@ private NotificationCompat.Action getAction( || player.getCurrentState() == Player.STATE_PREFLIGHT || player.getCurrentState() == Player.STATE_BLOCKED || player.getCurrentState() == Player.STATE_BUFFERING) { - return getAction(player, R.drawable.exo_notification_pause, + return getAction(player, R.drawable.exo_icon_pause, R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); } else { - return getAction(player, R.drawable.exo_notification_play, + return getAction(player, R.drawable.exo_icon_play, R.string.exo_controls_play_description, ACTION_PLAY_PAUSE); } @@ -324,10 +324,10 @@ private NotificationCompat.Action getAction( case NotificationConstants.SHUFFLE: if (player.getPlayQueue() != null && player.getPlayQueue().isShuffled()) { - return getAction(player, R.drawable.exo_controls_shuffle_on, + return getAction(player, R.drawable.exo_icon_shuffle_on, R.string.exo_controls_shuffle_on_description, ACTION_SHUFFLE); } else { - return getAction(player, R.drawable.exo_controls_shuffle_off, + return getAction(player, R.drawable.exo_icon_shuffle_off, R.string.exo_controls_shuffle_off_description, ACTION_SHUFFLE); } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index b9fba40c65..ea0c233108 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -21,7 +21,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.exoplayer2.PlaybackParameters; +import androidx.media3.common.PlaybackParameters; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ActivityPlayerQueueControlBinding; @@ -553,17 +553,17 @@ private void onStateChanged(final int state) { private void onPlayModeChanged(final int repeatMode, final boolean shuffled) { switch (repeatMode) { - case com.google.android.exoplayer2.Player.REPEAT_MODE_OFF: + case androidx.media3.common.Player.REPEAT_MODE_OFF: queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_off); + .setImageResource(R.drawable.exo_icon_repeat_off); break; - case com.google.android.exoplayer2.Player.REPEAT_MODE_ONE: + case androidx.media3.common.Player.REPEAT_MODE_ONE: queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_one); + .setImageResource(R.drawable.exo_icon_repeat_one); break; - case com.google.android.exoplayer2.Player.REPEAT_MODE_ALL: + case androidx.media3.common.Player.REPEAT_MODE_ALL: queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_all); + .setImageResource(R.drawable.exo_icon_repeat_all); break; } 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 23e803fee1..6a0773423d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1,18 +1,18 @@ package org.schabi.newpipe.player; -import static com.google.android.exoplayer2.PlaybackException.*; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_REMOVE; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SKIP; -import static com.google.android.exoplayer2.Player.DiscontinuityReason; -import static com.google.android.exoplayer2.Player.Listener; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static com.google.android.exoplayer2.Player.RepeatMode; +import static androidx.media3.common.PlaybackException.*; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_REMOVE; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_SKIP; +import static androidx.media3.common.Player.DiscontinuityReason; +import static androidx.media3.common.Player.Listener; +import static androidx.media3.common.Player.REPEAT_MODE_ALL; +import static androidx.media3.common.Player.REPEAT_MODE_OFF; +import static androidx.media3.common.Player.REPEAT_MODE_ONE; +import static androidx.media3.common.Player.RepeatMode; import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; @@ -78,22 +78,23 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.exoplayer2.*; -import com.google.android.exoplayer2.Player.PositionInfo; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.Tracks; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.TrackGroup; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.text.CueGroup; -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector; -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; -import com.google.android.exoplayer2.ui.CaptionStyleCompat; -import com.google.android.exoplayer2.ui.SubtitleView; -import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.VideoSize; +import androidx.media3.common.*; +import androidx.media3.exoplayer.*; +import androidx.media3.common.Player.PositionInfo; +import androidx.media3.common.Timeline; +import androidx.media3.common.Tracks; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.common.TrackGroup; +import androidx.media3.exoplayer.source.TrackGroupArray; +import androidx.media3.common.text.CueGroup; +import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; +import androidx.media3.exoplayer.trackselection.MappingTrackSelector; +import androidx.media3.ui.AspectRatioFrameLayout; +import androidx.media3.ui.CaptionStyleCompat; +import androidx.media3.ui.SubtitleView; +import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter; +import androidx.media3.common.util.Util; +import androidx.media3.common.VideoSize; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; @@ -794,7 +795,7 @@ public void handleIntent(@NonNull final Intent intent) { // Player can have state = IDLE when playback is stopped or failed // and we should retry in this case if (simpleExoPlayer.getPlaybackState() - == com.google.android.exoplayer2.Player.STATE_IDLE) { + == androidx.media3.common.Player.STATE_IDLE) { simpleExoPlayer.prepare(); } if (shouldSeek()) { @@ -811,7 +812,7 @@ public void handleIntent(@NonNull final Intent intent) { // Player can have state = IDLE when playback is stopped or failed // and we should retry in this case if (simpleExoPlayer.getPlaybackState() - == com.google.android.exoplayer2.Player.STATE_IDLE) { + == androidx.media3.common.Player.STATE_IDLE) { simpleExoPlayer.prepare(); } simpleExoPlayer.setPlayWhenReady(playWhenReady); @@ -2265,7 +2266,7 @@ public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason + "reason = [" + reason + "]"); } final int playbackState = exoPlayerIsNull() - ? com.google.android.exoplayer2.Player.STATE_IDLE + ? androidx.media3.common.Player.STATE_IDLE : simpleExoPlayer.getPlaybackState(); updatePlaybackState(playWhenReady, playbackState); } @@ -2294,22 +2295,22 @@ private void updatePlaybackState(final boolean playWhenReady, final int playback } switch (playbackState) { - case com.google.android.exoplayer2.Player.STATE_IDLE: // 1 + case androidx.media3.common.Player.STATE_IDLE: // 1 isPrepared = false; break; - case com.google.android.exoplayer2.Player.STATE_BUFFERING: // 2 + case androidx.media3.common.Player.STATE_BUFFERING: // 2 if (isPrepared) { changeState(STATE_BUFFERING); } break; - case com.google.android.exoplayer2.Player.STATE_READY: //3 + case androidx.media3.common.Player.STATE_READY: //3 if (!isPrepared) { isPrepared = true; onPrepared(playWhenReady); } changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED); break; - case com.google.android.exoplayer2.Player.STATE_ENDED: // 4 + case androidx.media3.common.Player.STATE_ENDED: // 4 changeState(STATE_COMPLETED); saveStreamProgressStateCompleted(); isPrepared = false; @@ -2826,13 +2827,13 @@ private void setRepeatModeButton(final AppCompatImageButton imageButton, @RepeatMode final int repeatMode) { switch (repeatMode) { case REPEAT_MODE_OFF: - imageButton.setImageResource(R.drawable.exo_controls_repeat_off); + imageButton.setImageResource(R.drawable.exo_icon_repeat_off); break; case REPEAT_MODE_ONE: - imageButton.setImageResource(R.drawable.exo_controls_repeat_one); + imageButton.setImageResource(R.drawable.exo_icon_repeat_one); break; case REPEAT_MODE_ALL: - imageButton.setImageResource(R.drawable.exo_controls_repeat_all); + imageButton.setImageResource(R.drawable.exo_icon_repeat_all); break; } } @@ -2920,13 +2921,13 @@ public void onScreenRotationButtonClicked() { * This is done because not all source resolution errors are {@link PlaybackException}, which * are also captured by {@link ExoPlayer} and stops the playback.

* - * @param player The {@link com.google.android.exoplayer2.Player} whose state changed. - * @param events The {@link com.google.android.exoplayer2.Player.Events} that has triggered + * @param player The {@link androidx.media3.common.Player} whose state changed. + * @param events The {@link androidx.media3.common.Player.Events} that has triggered * the player state changes. **/ @Override - public void onEvents(@NonNull final com.google.android.exoplayer2.Player player, - @NonNull final com.google.android.exoplayer2.Player.Events events) { + public void onEvents(@NonNull final androidx.media3.common.Player player, + @NonNull final androidx.media3.common.Player.Events events) { Listener.super.onEvents(player, events); MediaItemTag.from(player.getCurrentMediaItem()).ifPresent(tag -> { if (tag == currentMetadata) { @@ -3053,7 +3054,7 @@ public void onPrepare() { //region Errors /** - * Process exceptions produced by {@link com.google.android.exoplayer2.ExoPlayer ExoPlayer}. + * Process exceptions produced by {@link androidx.media3.exoplayer.ExoPlayer ExoPlayer}. *

There are multiple types of errors:

* - * @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException) + * @see androidx.media3.common.Player.Listener#onPlayerError(PlaybackException) * */ // Any error code not explicitly covered here are either unrelated to NewPipe use case // (e.g. DRM) or not recoverable (e.g. Decoder error). In both cases, the player should diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayerServiceForAuto.java b/app/src/main/java/org/schabi/newpipe/player/PlayerServiceForAuto.java index b529c34bac..85b0ac81ad 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayerServiceForAuto.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayerServiceForAuto.java @@ -38,7 +38,8 @@ import androidx.core.content.ContextCompat; import androidx.media.MediaBrowserServiceCompat; -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; +import android.net.Uri; +import android.support.v4.media.session.PlaybackStateCompat; import org.schabi.newpipe.App; import org.schabi.newpipe.databinding.PlayerBinding; import org.schabi.newpipe.player.mediabrowser.MediaBrowserImpl; @@ -71,7 +72,6 @@ public final class PlayerServiceForAuto extends MediaBrowserServiceCompat implem // these are instantiated in onCreate() as per // https://developer.android.com/training/cars/media#browser_workflow private MediaSessionCompat mediaSession; - private MediaSessionConnector sessionConnector; private Player player; private WindowManager windowManager; @@ -103,20 +103,44 @@ public void onCreate() { // see https://developer.android.com/training/cars/media#browser_workflow mediaSession = new MediaSessionCompat(this, "MediaSessionPlayerServ"); setSessionToken(mediaSession.getSessionToken()); - sessionConnector = new MediaSessionConnector(mediaSession); - sessionConnector.setMetadataDeduplicationEnabled(true); + mediaSession.setActive(true); mediaBrowserPlaybackPreparer = new MediaBrowserPlaybackPreparer( this, - sessionConnector::setCustomErrorMessage, - () -> sessionConnector.setCustomErrorMessage(null), + this::setSessionError, + () -> setSessionError(null, 0), (playWhenReady) -> { if (player != null) { player.onPrepare(); } } ); - sessionConnector.setPlaybackPreparer(mediaBrowserPlaybackPreparer); + mediaSession.setCallback(new MediaSessionCompat.Callback() { + @Override + public void onPrepare() { + mediaBrowserPlaybackPreparer.onPrepare(false); + } + + @Override + public void onPlayFromMediaId(final String mediaId, final Bundle extras) { + mediaBrowserPlaybackPreparer.onPrepareFromMediaId(mediaId, true, extras); + } + + @Override + public void onPrepareFromMediaId(final String mediaId, final Bundle extras) { + mediaBrowserPlaybackPreparer.onPrepareFromMediaId(mediaId, false, extras); + } + + @Override + public void onPlayFromSearch(final String query, final Bundle extras) { + mediaBrowserPlaybackPreparer.onPrepareFromSearch(query, true, extras); + } + + @Override + public void onPlayFromUri(final Uri uri, final Bundle extras) { + mediaBrowserPlaybackPreparer.onPrepareFromUri(uri, true, extras); + } + }); } private void createView() { @@ -302,6 +326,17 @@ public Player getPlayer() { return player; } + private void setSessionError(@Nullable final String message, final int code) { + final PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID); + if (message != null) { + builder.setState(PlaybackStateCompat.STATE_ERROR, 0, 1).setErrorMessage(code, message); + } else { + builder.setState(PlaybackStateCompat.STATE_NONE, 0, 1); + } + mediaSession.setPlaybackState(builder.build()); + } + /** * @return the media session for Android Auto compatibility */ diff --git a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java index 6cb23953d3..5417ddd51c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java @@ -7,11 +7,11 @@ package org.schabi.newpipe.player.datasource; -import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS; -import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS; -import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader; -import static com.google.android.exoplayer2.util.Assertions.checkNotNull; -import static com.google.android.exoplayer2.util.Util.castNonNull; +import static androidx.media3.datasource.DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS; +import static androidx.media3.datasource.DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS; +import static androidx.media3.datasource.HttpUtil.buildRangeRequestHeader; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Util.castNonNull; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getAndroidUserAgent; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getIosUserAgent; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isAndroidStreamingUrl; @@ -26,20 +26,20 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.metadata.icy.IcyHeaders; -import com.google.android.exoplayer2.upstream.BaseDataSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSourceException; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.HttpUtil; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.common.C; +import androidx.media3.common.PlaybackException; +import androidx.media3.extractor.metadata.icy.IcyHeaders; +import androidx.media3.datasource.BaseDataSource; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DataSourceException; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.DataSpec.HttpMethod; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.HttpUtil; +import androidx.media3.datasource.TransferListener; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import com.google.common.base.Predicate; import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMap; @@ -66,7 +66,7 @@ /** * An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}, based on - * {@link com.google.android.exoplayer2.upstream.DefaultHttpDataSource}, for YouTube streams. + * {@link androidx.media3.datasource.DefaultHttpDataSource}, for YouTube streams. * *

* It adds more headers to {@code videoplayback} URLs, such as {@code Origin}, {@code Referer} @@ -182,7 +182,7 @@ public Factory setAllowCrossProtocolRedirects( * *

* Note that it must be not enabled on streams which are using a {@link - * com.google.android.exoplayer2.source.ProgressiveMediaSource}, as it will break playback + * androidx.media3.exoplayer.source.ProgressiveMediaSource}, as it will break playback * for them (some exceptions may be thrown). *

* diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java index b5520e8bee..553b12c81f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java @@ -1,7 +1,7 @@ package org.schabi.newpipe.player.event; -import com.google.android.exoplayer2.PlaybackParameters; +import androidx.media3.common.PlaybackParameters; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueue; diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java index 359eab8b28..5b52455bf5 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceEventListener.java @@ -1,6 +1,6 @@ package org.schabi.newpipe.player.event; -import com.google.android.exoplayer2.PlaybackException; +import androidx.media3.common.PlaybackException; public interface PlayerServiceEventListener extends PlayerEventListener { void onFullscreenStateChanged(boolean fullscreen); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index 8613ef57bc..d880e6aed3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -15,8 +15,8 @@ import androidx.media.AudioManagerCompat; import androidx.preference.PreferenceManager; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.analytics.AnalyticsListener; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.exoplayer.analytics.AnalyticsListener; import org.schabi.newpipe.R; public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java b/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java index 202ff85412..dc5e520978 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java @@ -6,16 +6,16 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.database.StandaloneDatabaseProvider; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSource; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.FileDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.upstream.cache.CacheDataSink; -import com.google.android.exoplayer2.upstream.cache.CacheDataSource; -import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; -import com.google.android.exoplayer2.upstream.cache.SimpleCache; +import androidx.media3.database.StandaloneDatabaseProvider; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.datasource.FileDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.datasource.cache.CacheDataSink; +import androidx.media3.datasource.cache.CacheDataSource; +import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor; +import androidx.media3.datasource.cache.SimpleCache; import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/CustomMediaCodecVideoRenderer.java b/app/src/main/java/org/schabi/newpipe/player/helper/CustomMediaCodecVideoRenderer.java index 66ac6d50bc..5769346cd2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/CustomMediaCodecVideoRenderer.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/CustomMediaCodecVideoRenderer.java @@ -5,10 +5,10 @@ import androidx.annotation.Nullable; -import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter; -import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; -import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; -import com.google.android.exoplayer2.video.VideoRendererEventListener; +import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter; +import androidx.media3.exoplayer.mediacodec.MediaCodecSelector; +import androidx.media3.exoplayer.video.MediaCodecVideoRenderer; +import androidx.media3.exoplayer.video.VideoRendererEventListener; /** * A {@link MediaCodecVideoRenderer} which always enable the output surface workaround that diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/CustomRenderersFactory.java b/app/src/main/java/org/schabi/newpipe/player/helper/CustomRenderersFactory.java index 668b48c306..d7f4066a2c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/CustomRenderersFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/CustomRenderersFactory.java @@ -3,10 +3,10 @@ import android.content.Context; import android.os.Handler; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.Renderer; -import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; -import com.google.android.exoplayer2.video.VideoRendererEventListener; +import androidx.media3.exoplayer.DefaultRenderersFactory; +import androidx.media3.exoplayer.Renderer; +import androidx.media3.exoplayer.mediacodec.MediaCodecSelector; +import androidx.media3.exoplayer.video.VideoRendererEventListener; import java.util.ArrayList; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java index ec0e4e4a72..bcdcd96437 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java @@ -1,6 +1,6 @@ package org.schabi.newpipe.player.helper; -import com.google.android.exoplayer2.DefaultLoadControl; +import androidx.media3.exoplayer.DefaultLoadControl; public class LoadController extends DefaultLoadControl { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java index 72c1be0362..fa231b28c3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java @@ -2,6 +2,7 @@ import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -15,38 +16,54 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.media.session.MediaButtonReceiver; - -import com.google.android.exoplayer2.ForwardingPlayer; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector.PlaybackPreparer; +import androidx.media3.common.Player; +import androidx.media3.common.util.Util; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.player.NotificationUtil; import org.schabi.newpipe.player.mediasession.MediaSessionCallback; -import org.schabi.newpipe.player.mediasession.PlayQueueNavigator; +import org.schabi.newpipe.player.mediasession.PlaybackPreparer; import org.schabi.newpipe.player.playback.PlayerMediaSession; import org.schabi.newpipe.util.StreamTypeUtil; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Optional; -import static org.schabi.newpipe.player.PlayerService.*; +import static org.schabi.newpipe.player.PlayerService.ACTION_CHANGE_PLAY_MODE; +import static org.schabi.newpipe.player.PlayerService.ACTION_CLOSE; public class MediaSessionManager { private static final String TAG = MediaSessionManager.class.getSimpleName(); public static final boolean DEBUG = MainActivity.DEBUG; + private static final int MAX_QUEUE_SIZE = 10; + + private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_SEEK_TO + | PlaybackStateCompat.ACTION_PLAY + | PlaybackStateCompat.ACTION_PAUSE + | PlaybackStateCompat.ACTION_PLAY_PAUSE + | PlaybackStateCompat.ACTION_SKIP_TO_NEXT + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM + | PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_STOP + | PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID; + @NonNull private final MediaSessionCompat mediaSession; @NonNull - private final MediaSessionConnector sessionConnector; + private final Player exoPlayer; + @NonNull + private final MediaSessionCallback callback; + @Nullable + private final PlaybackPreparer playbackPreparer; private final boolean isExternalSession; - private int lastTitleHashCode; - private int lastArtistHashCode; - private long lastDuration; - private int lastAlbumArtHashCode; + @Nullable + private PlaybackStateCompat.CustomAction errorAction; private org.schabi.newpipe.player.Player player; @@ -68,90 +85,205 @@ public MediaSessionManager(@NonNull final Context context, @NonNull final MediaSessionCallback callback, @Nullable final MediaSessionCompat existingSession, @Nullable final PlaybackPreparer playbackPreparer) { - if (DEBUG) { - Log.d(TAG, "MediaSessionManager called"); - } - mediaSession = existingSession != null ? existingSession : new MediaSessionCompat(context, TAG); + this.exoPlayer = player; + this.callback = callback; + this.playbackPreparer = playbackPreparer; + mediaSession = existingSession != null ? existingSession + : new MediaSessionCompat(context, TAG); isExternalSession = existingSession != null; mediaSession.setActive(true); - mediaSession.setPlaybackState(new PlaybackStateCompat.Builder() - .setState(PlaybackStateCompat.STATE_NONE, -1, 1) - .setActions(PlaybackStateCompat.ACTION_SEEK_TO - | PlaybackStateCompat.ACTION_PLAY - | PlaybackStateCompat.ACTION_PAUSE // was play and pause now play/pause - | PlaybackStateCompat.ACTION_SKIP_TO_NEXT - | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS - | PlaybackStateCompat.ACTION_SET_REPEAT_MODE - | PlaybackStateCompat.ACTION_STOP - | PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID) - .build()); - - sessionConnector = new MediaSessionConnector(mediaSession); - sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback)); - sessionConnector.setPlayer(new ForwardingPlayer(player) { - @Override - public void play() { - callback.play(); + mediaSession.setCallback(sessionCallback); + + exoPlayer.addListener(playerListener); + updatePlaybackState(); + } + + private final Player.Listener playerListener = new Player.Listener() { + @Override + public void onEvents(@NonNull final Player p, @NonNull final Player.Events events) { + if (events.containsAny( + Player.EVENT_PLAYBACK_STATE_CHANGED, + Player.EVENT_PLAY_WHEN_READY_CHANGED, + Player.EVENT_IS_PLAYING_CHANGED, + Player.EVENT_POSITION_DISCONTINUITY, + Player.EVENT_REPEAT_MODE_CHANGED, + Player.EVENT_PLAYBACK_PARAMETERS_CHANGED)) { + updatePlaybackState(); + } + if (events.containsAny( + Player.EVENT_MEDIA_ITEM_TRANSITION, + Player.EVENT_MEDIA_METADATA_CHANGED, + Player.EVENT_TIMELINE_CHANGED)) { + updateMetadata(); + publishQueue(); } + } + }; + + private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() { + @Override + public void onPlay() { + callback.play(); + } - @Override - public void pause() { - callback.pause(); + @Override + public void onPause() { + callback.pause(); + } + + @Override + public void onSkipToNext() { + callback.playNext(); + } + + @Override + public void onSkipToPrevious() { + callback.playPrevious(); + } + + @Override + public void onSkipToQueueItem(final long id) { + callback.playItemAtIndex((int) id); + } + + @Override + public void onSeekTo(final long pos) { + exoPlayer.seekTo(pos); + } + + @Override + public void onStop() { + callback.close(); + } + + @Override + public void onCustomAction(final String action, final Bundle extras) { + if (ACTION_CHANGE_PLAY_MODE.equals(action)) { + callback.changePlayMode(); + } else if (ACTION_CLOSE.equals(action)) { + callback.close(); } - }); - MediaSessionConnector.CustomActionProvider[] providers = new MediaSessionConnector.CustomActionProvider[2]; - providers[0] = new MediaSessionConnector.CustomActionProvider() { - @Override - public void onCustomAction(@NonNull Player player, @NonNull String action, @Nullable Bundle extras) { - if (action.equals(ACTION_CHANGE_PLAY_MODE)) { - callback.changePlayMode(); - } + } + + @Override + public void onPrepare() { + if (playbackPreparer != null) { + playbackPreparer.onPrepare(false); } + } - @Nullable - @Override - public PlaybackStateCompat.CustomAction getCustomAction(Player player) { - switch (((PlayerMediaSession)callback).mode){ - case 0: - default: - return new android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder( - ACTION_CHANGE_PLAY_MODE, "Shuffle", R.drawable.shuffle_disabled).build(); - case 1: - return new android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder( - ACTION_CHANGE_PLAY_MODE, "Repeat all", R.drawable.exo_controls_shuffle_on).build(); - case 2: - return new android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder( - ACTION_CHANGE_PLAY_MODE, "Repeat none", R.drawable.exo_controls_repeat_one).build(); - case 3: - return new android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder( - ACTION_CHANGE_PLAY_MODE, "Repeat one", R.drawable.exo_controls_repeat_all).build(); - } + @Override + public void onPlayFromMediaId(final String mediaId, final Bundle extras) { + if (playbackPreparer != null) { + playbackPreparer.onPrepareFromMediaId(mediaId, true, extras); } - }; - providers[1] = new MediaSessionConnector.CustomActionProvider() { - @Override - public void onCustomAction(@NonNull Player player, @NonNull String action, @Nullable Bundle extras) { - if (action.equals(ACTION_CLOSE)) { - callback.close(); - } + } + + @Override + public void onPrepareFromMediaId(final String mediaId, final Bundle extras) { + if (playbackPreparer != null) { + playbackPreparer.onPrepareFromMediaId(mediaId, false, extras); } + } - @Nullable - @Override - public PlaybackStateCompat.CustomAction getCustomAction(Player player) { - // Close - return new android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder( - ACTION_CLOSE, "Close", R.drawable.ic_close).build(); + @Override + public void onPlayFromSearch(final String query, final Bundle extras) { + if (playbackPreparer != null) { + playbackPreparer.onPrepareFromSearch(query, true, extras); } - }; - sessionConnector.setCustomActionProviders(providers); - sessionConnector.setMetadataDeduplicationEnabled(true); - sessionConnector.setMediaMetadataProvider(exoPlayer -> buildMediaMetadata()); - - // Set PlaybackPreparer if provided (for Android Auto support) - if (playbackPreparer != null) { - sessionConnector.setPlaybackPreparer(playbackPreparer); } + + @Override + public void onPlayFromUri(final Uri uri, final Bundle extras) { + if (playbackPreparer != null) { + playbackPreparer.onPrepareFromUri(uri, true, extras); + } + } + }; + + private void updatePlaybackState() { + final PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() + .setActions(PLAYBACK_ACTIONS + | (playbackPreparer != null + ? playbackPreparer.getSupportedPrepareActions() : 0)) + .setState(toPlaybackStateCompat(), exoPlayer.getCurrentPosition(), + exoPlayer.getPlaybackParameters().speed); + builder.addCustomAction(changePlayModeAction()); + builder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder( + ACTION_CLOSE, "Close", R.drawable.ic_close).build()); + if (errorAction != null) { + builder.addCustomAction(errorAction); + } + mediaSession.setPlaybackState(builder.build()); + } + + @PlaybackStateCompat.State + private int toPlaybackStateCompat() { + switch (exoPlayer.getPlaybackState()) { + case Player.STATE_BUFFERING: + return PlaybackStateCompat.STATE_BUFFERING; + case Player.STATE_READY: + return exoPlayer.getPlayWhenReady() + ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED; + case Player.STATE_ENDED: + return PlaybackStateCompat.STATE_STOPPED; + case Player.STATE_IDLE: + default: + return PlaybackStateCompat.STATE_NONE; + } + } + + @NonNull + private PlaybackStateCompat.CustomAction changePlayModeAction() { + final int mode = callback instanceof PlayerMediaSession + ? ((PlayerMediaSession) callback).mode : 0; + switch (mode) { + case 1: + return new PlaybackStateCompat.CustomAction.Builder(ACTION_CHANGE_PLAY_MODE, + "Repeat all", R.drawable.exo_icon_shuffle_on).build(); + case 2: + return new PlaybackStateCompat.CustomAction.Builder(ACTION_CHANGE_PLAY_MODE, + "Repeat none", R.drawable.exo_icon_repeat_one).build(); + case 3: + return new PlaybackStateCompat.CustomAction.Builder(ACTION_CHANGE_PLAY_MODE, + "Repeat one", R.drawable.exo_icon_repeat_all).build(); + case 0: + default: + return new PlaybackStateCompat.CustomAction.Builder(ACTION_CHANGE_PLAY_MODE, + "Shuffle", R.drawable.shuffle_disabled).build(); + } + } + + private void publishQueue() { + final int windowCount = callback.getQueueSize(); + if (windowCount == 0) { + mediaSession.setQueue(Collections.emptyList()); + return; + } + final int currentWindowIndex = callback.getCurrentPlayingIndex(); + final int queueSize = Math.min(MAX_QUEUE_SIZE, windowCount); + final int startIndex = Util.constrainValue(currentWindowIndex - ((queueSize - 1) / 2), 0, + windowCount - queueSize); + + final List queue = new ArrayList<>(); + for (int i = startIndex; i < startIndex + queueSize; i++) { + queue.add(new MediaSessionCompat.QueueItem(callback.getQueueMetadata(i), i)); + } + mediaSession.setQueue(queue); + } + + /** Show an error on the session (used by the Android Auto media browser preparer). */ + public void setCustomErrorMessage(@Nullable final CharSequence message, final int code) { + errorAction = null; + final PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() + .setActions(PLAYBACK_ACTIONS) + .setState(PlaybackStateCompat.STATE_ERROR, 0, 1) + .setErrorMessage(code, message == null ? "" : message); + mediaSession.setPlaybackState(builder.build()); + } + + public void clearCustomErrorMessage() { + updatePlaybackState(); } @Nullable @@ -164,8 +296,13 @@ public MediaSessionCompat.Token getSessionToken() { return mediaSession.getSessionToken(); } - public void setPlayer(org.schabi.newpipe.player.Player player) { + public void setPlayer(final org.schabi.newpipe.player.Player player) { this.player = player; + updateMetadata(); + } + + private void updateMetadata() { + mediaSession.setMetadata(buildMediaMetadata()); } private MediaMetadataCompat buildMediaMetadata() { @@ -173,7 +310,7 @@ private MediaMetadataCompat buildMediaMetadata() { Log.d(TAG, "buildMediaMetadata called"); } - if(player == null) { + if (player == null) { return new MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "") .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "") @@ -219,8 +356,8 @@ private MediaMetadataCompat buildMediaMetadata() { * Should be called on player destruction to prevent leakage. */ public void dispose() { - sessionConnector.setPlayer(null); - sessionConnector.setQueueNavigator(null); + exoPlayer.removeListener(playerListener); + mediaSession.setCallback(null); if (!isExternalSession) { mediaSession.setActive(false); mediaSession.release(); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveDataSource.java index 608196fc44..124237678c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveDataSource.java @@ -20,19 +20,19 @@ import android.net.Uri; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.upstream.AssetDataSource; -import com.google.android.exoplayer2.upstream.ContentDataSource; -import com.google.android.exoplayer2.upstream.DataSchemeDataSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.FileDataSource; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.RawResourceDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.upstream.UdpDataSource; -import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.datasource.AssetDataSource; +import androidx.media3.datasource.ContentDataSource; +import androidx.media3.datasource.DataSchemeDataSource; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.FileDataSource; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.RawResourceDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.datasource.UdpDataSource; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -331,7 +331,7 @@ private DataSource getContentDataSource() { private DataSource getRtmpDataSource() { if (rtmpDataSource == null) { try { - Class clazz = Class.forName("com.google.android.exoplayer2.ext.rtmp.RtmpDataSource"); + Class clazz = Class.forName("androidx.media3.datasource.rtmp.RtmpDataSource"); rtmpDataSource = (DataSource) clazz.getConstructor().newInstance(); addListenersToDataSource(rtmpDataSource); } catch (ClassNotFoundException e) { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveHttpDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveHttpDataSource.java index 6f8a0c8893..62c71d4b5d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveHttpDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/NiconicoLiveHttpDataSource.java @@ -1,24 +1,14 @@ package org.schabi.newpipe.player.helper; -import android.net.Uri; - import androidx.annotation.Nullable; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.TransferListener; import com.google.common.base.Predicate; -import com.grack.nanojson.JsonParserException; - -import org.schabi.newpipe.extractor.exceptions.ParsingException; -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; -import java.io.IOException; -import java.util.Arrays; -import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Map; public class NiconicoLiveHttpDataSource extends PurifiedHttpDataSource { @@ -29,156 +19,73 @@ public class NiconicoLiveHttpDataSource extends PurifiedHttpDataSource { private boolean isFetching = false; public static class Factory implements HttpDataSource.Factory { - - private final RequestProperties defaultRequestProperties; + private final DefaultHttpDataSource.Factory inner = new DefaultHttpDataSource.Factory(); private final String url; - @Nullable - private TransferListener transferListener; - @Nullable private Predicate contentTypePredicate; - @Nullable private String userAgent; - private int connectTimeoutMs; - private int readTimeoutMs; - private boolean allowCrossProtocolRedirects; - private boolean keepPostFor302Redirects; - /** Creates an instance. */ - public Factory(String url) { - if(url.equals("")){ - throw(new RuntimeException("Build NicoNico live source failed. This should never happen.")); + public Factory(final String url) { + if (url.equals("")) { + throw new RuntimeException( + "Build NicoNico live source failed. This should never happen."); } - defaultRequestProperties = new RequestProperties(); - connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS; - readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS; this.url = url; } @Override - public final NiconicoLiveHttpDataSource.Factory setDefaultRequestProperties(Map defaultRequestProperties) { - this.defaultRequestProperties.clearAndSet(defaultRequestProperties); + public final Factory setDefaultRequestProperties( + final Map defaultRequestProperties) { + inner.setDefaultRequestProperties(defaultRequestProperties); return this; } - /** - * Sets the user agent that will be used. - * - *

The default is {@code null}, which causes the default user agent of the underlying - * platform to be used. - * - * @param userAgent The user agent that will be used, or {@code null} to use the default user - * agent of the underlying platform. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setUserAgent(@Nullable String userAgent) { - this.userAgent = userAgent; + public Factory setUserAgent(@Nullable final String userAgent) { + inner.setUserAgent(userAgent); return this; } - /** - * Sets the connect timeout, in milliseconds. - * - *

The default is {@link PurifiedHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}. - * - * @param connectTimeoutMs The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setConnectTimeoutMs(int connectTimeoutMs) { - this.connectTimeoutMs = connectTimeoutMs; + public Factory setConnectTimeoutMs(final int connectTimeoutMs) { + inner.setConnectTimeoutMs(connectTimeoutMs); return this; } - /** - * Sets the read timeout, in milliseconds. - * - *

The default is {@link PurifiedHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS}. - * - * @param readTimeoutMs The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setReadTimeoutMs(int readTimeoutMs) { - this.readTimeoutMs = readTimeoutMs; + public Factory setReadTimeoutMs(final int readTimeoutMs) { + inner.setReadTimeoutMs(readTimeoutMs); return this; } - /** - * Sets whether to allow cross protocol redirects. - * - *

The default is {@code false}. - * - * @param allowCrossProtocolRedirects Whether to allow cross protocol redirects. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) { - this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + public Factory setAllowCrossProtocolRedirects(final boolean allowCrossProtocolRedirects) { + inner.setAllowCrossProtocolRedirects(allowCrossProtocolRedirects); return this; } - /** - * Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a - * {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link - * PurifiedHttpDataSource#open(DataSpec)}. - * - *

The default is {@code null}. - * - * @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a - * predicate that was previously set. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setContentTypePredicate(@Nullable Predicate contentTypePredicate) { - this.contentTypePredicate = contentTypePredicate; + public Factory setContentTypePredicate(@Nullable final Predicate predicate) { + inner.setContentTypePredicate(predicate); return this; } - /** - * Sets the {@link TransferListener} that will be used. - * - *

The default is {@code null}. - * - *

See {@link DataSource#addTransferListener(TransferListener)}. - * - * @param transferListener The listener that will be used. - * @return This factory. - */ - public NiconicoLiveHttpDataSource.Factory setTransferListener(@Nullable TransferListener transferListener) { - this.transferListener = transferListener; + public Factory setTransferListener(@Nullable final TransferListener transferListener) { + inner.setTransferListener(transferListener); return this; } - /** - * Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a - * POST request. - */ - public NiconicoLiveHttpDataSource.Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) { - this.keepPostFor302Redirects = keepPostFor302Redirects; + public Factory setKeepPostFor302Redirects(final boolean keepPostFor302Redirects) { + inner.setKeepPostFor302Redirects(keepPostFor302Redirects); return this; } @Override public NiconicoLiveHttpDataSource createDataSource() { - NiconicoLiveHttpDataSource dataSource = - new NiconicoLiveHttpDataSource( - userAgent, - connectTimeoutMs, - readTimeoutMs, - allowCrossProtocolRedirects, - defaultRequestProperties, - contentTypePredicate, - keepPostFor302Redirects, - url); - if (transferListener != null) { - dataSource.addTransferListener(transferListener); - } - return dataSource; + return new NiconicoLiveHttpDataSource(inner.createDataSource(), url); } } - NiconicoLiveHttpDataSource(@Nullable String userAgent, int connectTimeoutMillis, int readTimeoutMillis, boolean allowCrossProtocolRedirects, @Nullable RequestProperties defaultRequestProperties - , @Nullable Predicate contentTypePredicate, boolean keepPostFor302Redirects, String liveUrl) { - super(userAgent, connectTimeoutMillis, readTimeoutMillis, allowCrossProtocolRedirects, defaultRequestProperties, contentTypePredicate, keepPostFor302Redirects); + + NiconicoLiveHttpDataSource(final HttpDataSource delegate, final String liveUrl) { + super(delegate); this.liveUrl = liveUrl; } + @Override - public long open(DataSpec dataSpec) throws HttpDataSourceException - { + public long open(final DataSpec dataSpec) throws HttpDataSourceException { // String fetchUrl = dataSpec.uri.toString(); // int type = 0; // List anonStrings = Arrays.asList("anonymous-user-", "anonymous_user_", "ht2_nicolive="); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/NonUriHlsPlaylistParserFactory.java b/app/src/main/java/org/schabi/newpipe/player/helper/NonUriHlsPlaylistParserFactory.java index a3a25fd1df..aee0d5ff46 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/NonUriHlsPlaylistParserFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/NonUriHlsPlaylistParserFactory.java @@ -5,11 +5,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; -import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist; -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParserFactory; -import com.google.android.exoplayer2.upstream.ParsingLoadable; +import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist; +import androidx.media3.exoplayer.hls.playlist.HlsMultivariantPlaylist; +import androidx.media3.exoplayer.hls.playlist.HlsPlaylist; +import androidx.media3.exoplayer.hls.playlist.HlsPlaylistParserFactory; +import androidx.media3.exoplayer.upstream.ParsingLoadable; import java.io.IOException; import java.io.InputStream; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java index 0217153e3f..fa9c4d1039 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java @@ -3,24 +3,24 @@ import android.content.Context; import android.net.Uri; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.source.SingleSampleMediaSource; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.hls.DefaultHlsExtractorFactory; -import com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTracker; -import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; -import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSource; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; -import com.google.android.exoplayer2.upstream.ResolvingDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.upstream.cache.CacheDataSource; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.source.ProgressiveMediaSource; +import androidx.media3.exoplayer.source.SingleSampleMediaSource; +import androidx.media3.exoplayer.dash.DashMediaSource; +import androidx.media3.exoplayer.dash.DefaultDashChunkSource; +import androidx.media3.exoplayer.hls.HlsMediaSource; +import androidx.media3.exoplayer.hls.DefaultHlsExtractorFactory; +import androidx.media3.exoplayer.hls.playlist.DefaultHlsPlaylistTracker; +import androidx.media3.exoplayer.smoothstreaming.DefaultSsChunkSource; +import androidx.media3.exoplayer.smoothstreaming.SsMediaSource; +import androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy; +import androidx.media3.datasource.ResolvingDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.datasource.cache.CacheDataSource; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; @@ -48,7 +48,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParserFactory; +import androidx.media3.exoplayer.hls.playlist.HlsPlaylistParserFactory; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubePostLiveStreamDvrDashManifestCreator; @@ -117,10 +117,11 @@ public HlsMediaSource.Factory getLiveHlsMediaSourceFactory() { return new HlsMediaSource.Factory(cachelessDataSourceFactory) .setAllowChunklessPreparation(true) .setPlaylistTrackerFactory((dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory) -> + playlistParserFactory, cmcdConfiguration, + downloadExecutorSupplier) -> new DefaultHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory, - PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT)); + playlistParserFactory, cmcdConfiguration, + PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT, downloadExecutorSupplier)); } public DashMediaSource.Factory getLiveDashMediaSourceFactory() { @@ -257,10 +258,12 @@ public HlsMediaSource.Factory getNicoLiveHlsMediaSourceFactory(String liveUrl) { return new HlsMediaSource.Factory(newFactory) .setAllowChunklessPreparation(true) .setPlaylistTrackerFactory((dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory) -> + playlistParserFactory, cmcdConfiguration, + downloadExecutorSupplier) -> new DefaultHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory, - PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT)).setLoadErrorHandlingPolicy(new DefaultLoadErrorHandlingPolicy()); + playlistParserFactory, cmcdConfiguration, + PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT, downloadExecutorSupplier)) + .setLoadErrorHandlingPolicy(new DefaultLoadErrorHandlingPolicy()); } // BiliBiliMediaSourceFactories diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 91258be878..b2c2778a77 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -1,8 +1,8 @@ package org.schabi.newpipe.player.helper; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; +import static androidx.media3.common.Player.REPEAT_MODE_ALL; +import static androidx.media3.common.Player.REPEAT_MODE_OFF; +import static androidx.media3.common.Player.REPEAT_MODE_ONE; import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; import static org.schabi.newpipe.extractor.stream.VideoStream.RESOLUTION_UNKNOWN; import static org.schabi.newpipe.player.Player.IDLE_WINDOW_FLAGS; @@ -33,16 +33,16 @@ import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player.RepeatMode; -import com.google.android.exoplayer2.SeekParameters; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; -import com.google.android.exoplayer2.trackselection.ExoTrackSelection; -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; -import com.google.android.exoplayer2.ui.CaptionStyleCompat; -import com.google.android.exoplayer2.util.MimeTypes; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player.RepeatMode; +import androidx.media3.exoplayer.SeekParameters; +import androidx.media3.exoplayer.source.ProgressiveMediaSource; +import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection; +import androidx.media3.exoplayer.trackselection.ExoTrackSelection; +import androidx.media3.ui.AspectRatioFrameLayout; +import androidx.media3.ui.AspectRatioFrameLayout.ResizeMode; +import androidx.media3.ui.CaptionStyleCompat; +import androidx.media3.common.MimeTypes; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java index b853cb37f6..33390b078b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java @@ -8,8 +8,8 @@ import android.util.Log; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; import org.schabi.newpipe.App; import org.schabi.newpipe.MainActivity; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedDataSource.java index 903405c3af..c76a5163c6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedDataSource.java @@ -20,19 +20,19 @@ import android.net.Uri; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.upstream.AssetDataSource; -import com.google.android.exoplayer2.upstream.ContentDataSource; -import com.google.android.exoplayer2.upstream.DataSchemeDataSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.FileDataSource; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.RawResourceDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; -import com.google.android.exoplayer2.upstream.UdpDataSource; -import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.datasource.AssetDataSource; +import androidx.media3.datasource.ContentDataSource; +import androidx.media3.datasource.DataSchemeDataSource; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.FileDataSource; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.RawResourceDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.datasource.UdpDataSource; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -331,7 +331,7 @@ private DataSource getContentDataSource() { private DataSource getRtmpDataSource() { if (rtmpDataSource == null) { try { - Class clazz = Class.forName("com.google.android.exoplayer2.ext.rtmp.RtmpDataSource"); + Class clazz = Class.forName("androidx.media3.datasource.rtmp.RtmpDataSource"); rtmpDataSource = (DataSource) clazz.getConstructor().newInstance(); addListenersToDataSource(rtmpDataSource); } catch (ClassNotFoundException e) { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedHttpDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedHttpDataSource.java index 6fc957fcc5..162145dc9f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedHttpDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PurifiedHttpDataSource.java @@ -1,182 +1,139 @@ package org.schabi.newpipe.player.helper; +import android.net.Uri; + import androidx.annotation.Nullable; -import com.google.android.exoplayer2.metadata.icy.IcyHeaders; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.TransferListener; +import androidx.media3.datasource.DataSpec; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.datasource.HttpDataSource; +import androidx.media3.datasource.TransferListener; +import androidx.media3.extractor.metadata.icy.IcyHeaders; import com.google.common.base.Predicate; import java.util.HashMap; +import java.util.List; import java.util.Map; -public class PurifiedHttpDataSource extends DefaultHttpDataSource { - public static class Factory implements HttpDataSource.Factory { +/** + * media3 makes {@link DefaultHttpDataSource}'s constructor private, so we can no longer subclass it. + * We wrap one instead and only intercept {@link #open(DataSpec)} to drop the ICY metadata-enable + * header. Subclasses (NiconicoLive) override {@code open} and call {@code super.open}. + */ +public class PurifiedHttpDataSource implements HttpDataSource { + public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = + DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS; + public static final int DEFAULT_READ_TIMEOUT_MILLIS = + DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS; + + protected final HttpDataSource delegate; + + protected PurifiedHttpDataSource(final HttpDataSource delegate) { + this.delegate = delegate; + } - private final RequestProperties defaultRequestProperties; - - @Nullable - private TransferListener transferListener; - @Nullable private Predicate contentTypePredicate; - @Nullable private String userAgent; - private int connectTimeoutMs; - private int readTimeoutMs; - private boolean allowCrossProtocolRedirects; - private boolean keepPostFor302Redirects; - - /** Creates an instance. */ - public Factory() { - defaultRequestProperties = new RequestProperties(); - connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS; - readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS; - } + @Override + public long open(final DataSpec dataSpec) throws HttpDataSourceException { + final Map headers = new HashMap<>(dataSpec.httpRequestHeaders); + headers.remove(IcyHeaders.REQUEST_HEADER_ENABLE_METADATA_NAME); + return delegate.open(dataSpec.withRequestHeaders(headers)); + } + + @Override + public int read(final byte[] buffer, final int offset, final int length) + throws HttpDataSourceException { + return delegate.read(buffer, offset, length); + } + + @Override + public void addTransferListener(final TransferListener transferListener) { + delegate.addTransferListener(transferListener); + } + + @Nullable + @Override + public Uri getUri() { + return delegate.getUri(); + } + + @Override + public Map> getResponseHeaders() { + return delegate.getResponseHeaders(); + } + + @Override + public void close() throws HttpDataSourceException { + delegate.close(); + } + + @Override + public void setRequestProperty(final String name, final String value) { + delegate.setRequestProperty(name, value); + } + + @Override + public void clearRequestProperty(final String name) { + delegate.clearRequestProperty(name); + } + + @Override + public void clearAllRequestProperties() { + delegate.clearAllRequestProperties(); + } + + @Override + public int getResponseCode() { + return delegate.getResponseCode(); + } + + public static class Factory implements HttpDataSource.Factory { + protected final DefaultHttpDataSource.Factory inner = new DefaultHttpDataSource.Factory(); @Override - public final PurifiedHttpDataSource.Factory setDefaultRequestProperties(Map defaultRequestProperties) { - this.defaultRequestProperties.clearAndSet(defaultRequestProperties); + public final Factory setDefaultRequestProperties( + final Map defaultRequestProperties) { + inner.setDefaultRequestProperties(defaultRequestProperties); return this; } - /** - * Sets the user agent that will be used. - * - *

The default is {@code null}, which causes the default user agent of the underlying - * platform to be used. - * - * @param userAgent The user agent that will be used, or {@code null} to use the default user - * agent of the underlying platform. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setUserAgent(@Nullable String userAgent) { - this.userAgent = userAgent; + public Factory setUserAgent(@Nullable final String userAgent) { + inner.setUserAgent(userAgent); return this; } - /** - * Sets the connect timeout, in milliseconds. - * - *

The default is {@link PurifiedHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}. - * - * @param connectTimeoutMs The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setConnectTimeoutMs(int connectTimeoutMs) { - this.connectTimeoutMs = connectTimeoutMs; + public Factory setConnectTimeoutMs(final int connectTimeoutMs) { + inner.setConnectTimeoutMs(connectTimeoutMs); return this; } - /** - * Sets the read timeout, in milliseconds. - * - *

The default is {@link PurifiedHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS}. - * - * @param readTimeoutMs The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setReadTimeoutMs(int readTimeoutMs) { - this.readTimeoutMs = readTimeoutMs; + public Factory setReadTimeoutMs(final int readTimeoutMs) { + inner.setReadTimeoutMs(readTimeoutMs); return this; } - /** - * Sets whether to allow cross protocol redirects. - * - *

The default is {@code false}. - * - * @param allowCrossProtocolRedirects Whether to allow cross protocol redirects. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) { - this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + public Factory setAllowCrossProtocolRedirects(final boolean allowCrossProtocolRedirects) { + inner.setAllowCrossProtocolRedirects(allowCrossProtocolRedirects); return this; } - /** - * Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a - * {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link - * PurifiedHttpDataSource#open(DataSpec)}. - * - *

The default is {@code null}. - * - * @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a - * predicate that was previously set. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setContentTypePredicate(@Nullable Predicate contentTypePredicate) { - this.contentTypePredicate = contentTypePredicate; + public Factory setContentTypePredicate(@Nullable final Predicate predicate) { + inner.setContentTypePredicate(predicate); return this; } - /** - * Sets the {@link TransferListener} that will be used. - * - *

The default is {@code null}. - * - *

See {@link DataSource#addTransferListener(TransferListener)}. - * - * @param transferListener The listener that will be used. - * @return This factory. - */ - public PurifiedHttpDataSource.Factory setTransferListener(@Nullable TransferListener transferListener) { - this.transferListener = transferListener; + public Factory setTransferListener(@Nullable final TransferListener transferListener) { + inner.setTransferListener(transferListener); return this; } - /** - * Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a - * POST request. - */ - public PurifiedHttpDataSource.Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) { - this.keepPostFor302Redirects = keepPostFor302Redirects; + public Factory setKeepPostFor302Redirects(final boolean keepPostFor302Redirects) { + inner.setKeepPostFor302Redirects(keepPostFor302Redirects); return this; } @Override public PurifiedHttpDataSource createDataSource() { - PurifiedHttpDataSource dataSource = - new PurifiedHttpDataSource( - userAgent, - connectTimeoutMs, - readTimeoutMs, - allowCrossProtocolRedirects, - defaultRequestProperties, - contentTypePredicate, - keepPostFor302Redirects); - if (transferListener != null) { - dataSource.addTransferListener(transferListener); - } - return dataSource; + return new PurifiedHttpDataSource(inner.createDataSource()); } } - PurifiedHttpDataSource( - @Nullable String userAgent, - int connectTimeoutMillis, - int readTimeoutMillis, - boolean allowCrossProtocolRedirects, - @Nullable RequestProperties defaultRequestProperties, - @Nullable Predicate contentTypePredicate, - boolean keepPostFor302Redirects) { - super( - userAgent, - connectTimeoutMillis, - readTimeoutMillis, - allowCrossProtocolRedirects, - defaultRequestProperties - ); - } - - @Override - public long open(DataSpec dataSpec) throws HttpDataSourceException - { - final Map m1 = dataSpec.httpRequestHeaders; - final Map m2 = new HashMap<>(); - for (Map.Entry entry : m1.entrySet()) - if(!entry.getKey().equals(IcyHeaders.REQUEST_HEADER_ENABLE_METADATA_NAME)) - m2.put(entry.getKey(), entry.getValue()); - - return super.open(dataSpec.withRequestHeaders(m2)); - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java b/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java index 00f5de071c..efaf5c91d7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java @@ -5,12 +5,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.source.dash.manifest.DashManifest; -import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; -import com.google.android.exoplayer2.source.dash.manifest.Period; -import com.google.android.exoplayer2.source.dash.manifest.ProgramInformation; -import com.google.android.exoplayer2.source.dash.manifest.ServiceDescriptionElement; -import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement; +import androidx.media3.exoplayer.dash.manifest.DashManifest; +import androidx.media3.exoplayer.dash.manifest.DashManifestParser; +import androidx.media3.exoplayer.dash.manifest.Period; +import androidx.media3.exoplayer.dash.manifest.ProgramInformation; +import androidx.media3.exoplayer.dash.manifest.ServiceDescriptionElement; +import androidx.media3.exoplayer.dash.manifest.UtcTimingElement; import java.util.List; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt index 129f248875..044e7b4b4c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt +++ b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt @@ -3,11 +3,9 @@ package org.schabi.newpipe.player.mediabrowser import android.content.Context import android.net.Uri import android.os.Bundle -import android.os.ResultReceiver import android.support.v4.media.session.PlaybackStateCompat import android.util.Log -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector.PlaybackPreparer +import org.schabi.newpipe.player.mediasession.PlaybackPreparer import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.Disposable @@ -94,15 +92,6 @@ class MediaBrowserPlaybackPreparer( override fun onPrepareFromUri(uri: Uri, playWhenReady: Boolean, extras: Bundle?) { onUnsupportedError() } - - override fun onCommand( - player: Player, - command: String, - extras: Bundle?, - cb: ResultReceiver? - ): Boolean { - return false - } //endregion //region Errors diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java index d23dd4574c..1e00e2b8f8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java @@ -2,10 +2,10 @@ import android.net.Uri; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.MediaItem.RequestMetadata; -import com.google.android.exoplayer2.MediaMetadata; -import com.google.android.exoplayer2.Player; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaItem.RequestMetadata; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.Player; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java index 4095f2bc88..f2ed6dc138 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java @@ -1,6 +1,6 @@ package org.schabi.newpipe.player.mediaitem; -import com.google.android.exoplayer2.MediaItem; +import androidx.media3.common.MediaItem; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java deleted file mode 100644 index 92cd425c5f..0000000000 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.schabi.newpipe.player.mediasession; - -import android.os.Bundle; -import android.os.ResultReceiver; -import android.support.v4.media.session.MediaSessionCompat; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; -import com.google.android.exoplayer2.util.Util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM; - -public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator { - public static final int DEFAULT_MAX_QUEUE_SIZE = 10; - - private final MediaSessionCompat mediaSession; - private final MediaSessionCallback callback; - private final int maxQueueSize; - - private long activeQueueItemId; - - public PlayQueueNavigator(@NonNull final MediaSessionCompat mediaSession, - @NonNull final MediaSessionCallback callback) { - this.mediaSession = mediaSession; - this.callback = callback; - this.maxQueueSize = DEFAULT_MAX_QUEUE_SIZE; - - this.activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; - } - - @Override - public long getSupportedQueueNavigatorActions(@Nullable final Player player) { - return ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS | ACTION_SKIP_TO_QUEUE_ITEM; - } - - @Override - public void onTimelineChanged(@NonNull final Player player) { - publishFloatingQueueWindow(); - } - - @Override - public void onCurrentMediaItemIndexChanged(@NonNull final Player player) { - if (activeQueueItemId == MediaSessionCompat.QueueItem.UNKNOWN_ID - || player.getCurrentTimeline().getWindowCount() > maxQueueSize) { - publishFloatingQueueWindow(); - } else if (!player.getCurrentTimeline().isEmpty()) { - activeQueueItemId = player.getCurrentMediaItemIndex(); - } - } - - @Override - public long getActiveQueueItemId(@Nullable final Player player) { - return callback.getCurrentPlayingIndex(); - } - - @Override - public void onSkipToPrevious(@NonNull final Player player) { - callback.playPrevious(); - } - - @Override - public void onSkipToQueueItem(@NonNull final Player player, final long id) { - callback.playItemAtIndex((int) id); - } - - @Override - public void onSkipToNext(@NonNull final Player player) { - callback.playNext(); - } - - private void publishFloatingQueueWindow() { - if (callback.getQueueSize() == 0) { - mediaSession.setQueue(Collections.emptyList()); - activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; - return; - } - - // Yes this is almost a copypasta, got a problem with that? =\ - final int windowCount = callback.getQueueSize(); - final int currentWindowIndex = callback.getCurrentPlayingIndex(); - final int queueSize = Math.min(maxQueueSize, windowCount); - final int startIndex = Util.constrainValue(currentWindowIndex - ((queueSize - 1) / 2), 0, - windowCount - queueSize); - - final List queue = new ArrayList<>(); - for (int i = startIndex; i < startIndex + queueSize; i++) { - queue.add(new MediaSessionCompat.QueueItem(callback.getQueueMetadata(i), i)); - } - mediaSession.setQueue(queue); - activeQueueItemId = currentWindowIndex; - } - - @Override - public boolean onCommand(@NonNull final Player player, - @NonNull final String command, - @Nullable final Bundle extras, - @Nullable final ResultReceiver cb) { - return false; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlaybackPreparer.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlaybackPreparer.java new file mode 100644 index 0000000000..8be402b782 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlaybackPreparer.java @@ -0,0 +1,22 @@ +package org.schabi.newpipe.player.mediasession; + +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.Nullable; + +/** + * Replacement for ExoPlayer's removed {@code MediaSessionConnector.PlaybackPreparer}: lets the + * media browser (Android Auto) turn a media id / search / uri into actual playback. + */ +public interface PlaybackPreparer { + long getSupportedPrepareActions(); + + void onPrepare(boolean playWhenReady); + + void onPrepareFromMediaId(String mediaId, boolean playWhenReady, @Nullable Bundle extras); + + void onPrepareFromSearch(String query, boolean playWhenReady, @Nullable Bundle extras); + + void onPrepareFromUri(Uri uri, boolean playWhenReady, @Nullable Bundle extras); +} diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java index 8aad356d0a..9e00cf3a7d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java @@ -2,15 +2,15 @@ import android.util.Log; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.BaseMediaSource; -import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.SilenceMediaSource; -import com.google.android.exoplayer2.source.SinglePeriodTimeline; -import com.google.android.exoplayer2.upstream.Allocator; -import com.google.android.exoplayer2.upstream.TransferListener; +import androidx.media3.common.MediaItem; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.Timeline; +import androidx.media3.exoplayer.source.BaseMediaSource; +import androidx.media3.exoplayer.source.MediaPeriod; +import androidx.media3.exoplayer.source.SilenceMediaSource; +import androidx.media3.exoplayer.source.SinglePeriodTimeline; +import androidx.media3.exoplayer.upstream.Allocator; +import androidx.media3.datasource.TransferListener; import org.schabi.newpipe.player.mediaitem.ExceptionTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -120,10 +120,10 @@ protected void prepareSourceInternal(@Nullable final TransferListener mediaTrans * If the error is not known, e.g. network issue, then the exception is not swallowed here in * {@link FailedMediaSource}. The exception is then propagated to the player, which * {@link org.schabi.newpipe.player.Player Player} can react to inside - * {@link com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException)}. + * {@link androidx.media3.common.Player.Listener#onPlayerError(PlaybackException)}. * * @throws IOException An error which will always result in - * {@link com.google.android.exoplayer2.PlaybackException#ERROR_CODE_IO_UNSPECIFIED}. + * {@link androidx.media3.common.PlaybackException#ERROR_CODE_IO_UNSPECIFIED}. */ @Override public void maybeThrowSourceInfoRefreshError() throws IOException { diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java index 95524cf692..2832be7fa6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java @@ -1,12 +1,12 @@ package org.schabi.newpipe.player.mediasource; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.CompositeMediaSource; -import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.upstream.Allocator; -import com.google.android.exoplayer2.upstream.TransferListener; +import androidx.media3.common.MediaItem; +import androidx.media3.common.Timeline; +import androidx.media3.exoplayer.source.CompositeMediaSource; +import androidx.media3.exoplayer.source.MediaPeriod; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.upstream.Allocator; +import androidx.media3.datasource.TransferListener; import org.schabi.newpipe.player.mediaitem.MediaItemTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java index 9d6b948937..f60bd49815 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java @@ -2,7 +2,7 @@ import androidx.annotation.NonNull; -import com.google.android.exoplayer2.source.MediaSource; +import androidx.media3.exoplayer.source.MediaSource; import org.schabi.newpipe.player.playqueue.PlayQueueItem; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java index 4c03807672..46335fd075 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java @@ -5,8 +5,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.ShuffleOrder; +import androidx.media3.exoplayer.source.ConcatenatingMediaSource; +import androidx.media3.exoplayer.source.ShuffleOrder; import org.schabi.newpipe.player.mediaitem.MediaItemTag; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java index 92d4403c8b..4e18edf6f1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java @@ -1,11 +1,11 @@ package org.schabi.newpipe.player.mediasource; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.CompositeMediaSource; -import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.upstream.Allocator; +import androidx.media3.common.MediaItem; +import androidx.media3.common.Timeline; +import androidx.media3.exoplayer.source.CompositeMediaSource; +import androidx.media3.exoplayer.source.MediaPeriod; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.upstream.Allocator; import org.schabi.newpipe.player.mediaitem.PlaceholderTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index f984668276..5269771b36 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -8,7 +8,7 @@ import androidx.annotation.Nullable; import androidx.collection.ArraySet; -import com.google.android.exoplayer2.source.MediaSource; +import androidx.media3.exoplayer.source.MediaSource; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -477,7 +477,7 @@ private void onMediaSourceReceived(@NonNull final PlayQueueItem item, /** * Checks if the corresponding MediaSource in - * {@link com.google.android.exoplayer2.source.ConcatenatingMediaSource} + * {@link androidx.media3.exoplayer.source.ConcatenatingMediaSource} * for a given {@link PlayQueueItem} needs replacement, either due to gapless playback * readiness or playlist desynchronization. *

diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java index 7376070015..e482646026 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java @@ -3,7 +3,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.source.MediaSource; +import androidx.media3.exoplayer.source.MediaSource; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueueItem; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java index cd0f377155..265c50a1c7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java @@ -9,15 +9,15 @@ import org.schabi.newpipe.player.mediasession.MediaSessionCallback; import org.schabi.newpipe.player.playqueue.PlayQueueItem; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; +import static androidx.media3.common.Player.REPEAT_MODE_OFF; import static org.schabi.newpipe.player.helper.PlayerHelper.nextRepeatMode; public class PlayerMediaSession implements MediaSessionCallback { public final Player player; - private final com.google.android.exoplayer2.Player exoPlayer; + private final androidx.media3.common.Player exoPlayer; public int mode = 0; - public PlayerMediaSession(final Player player, final com.google.android.exoplayer2.Player exoPlayer) { + public PlayerMediaSession(final Player player, final androidx.media3.common.Player exoPlayer) { this.player = player; this.exoPlayer = exoPlayer; refresh(); @@ -114,10 +114,10 @@ public void changePlayMode() { player.onShuffleClicked(); break; case 1: // repeat_one - player.setRepeatMode(com.google.android.exoplayer2.Player.REPEAT_MODE_ONE); + player.setRepeatMode(androidx.media3.common.Player.REPEAT_MODE_ONE); break; case 2: // repeat_all - player.setRepeatMode(com.google.android.exoplayer2.Player.REPEAT_MODE_ALL); + player.setRepeatMode(androidx.media3.common.Player.REPEAT_MODE_ALL); break; case 3: // repeat_none default: @@ -132,9 +132,9 @@ public void close(){ public void refresh(){ if (exoPlayer.getShuffleModeEnabled()) { this.mode = 1; - } else if (exoPlayer.getRepeatMode() == com.google.android.exoplayer2.Player.REPEAT_MODE_ONE) { + } else if (exoPlayer.getRepeatMode() == androidx.media3.common.Player.REPEAT_MODE_ONE) { this.mode = 2; - } else if (exoPlayer.getRepeatMode() == com.google.android.exoplayer2.Player.REPEAT_MODE_ALL) { + } else if (exoPlayer.getRepeatMode() == androidx.media3.common.Player.REPEAT_MODE_ALL) { this.mode = 3; } else { this.mode = 0; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java b/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java index da6cb36d4f..b02a9240d3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java @@ -3,8 +3,8 @@ import android.content.Context; import android.view.SurfaceHolder; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.video.PlaceholderSurface; +import androidx.media3.common.Player; +import androidx.media3.exoplayer.video.PlaceholderSurface; /** * Prevent error message: 'Unrecoverable player error occurred' 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 7e3f590078..d44b64b556 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 @@ -9,7 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.source.MediaSource; +import androidx.media3.exoplayer.source.MediaSource; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/CustomDataSourceFactory.java b/app/src/main/java/org/schabi/newpipe/player/resolver/CustomDataSourceFactory.java index 82dff6ec9b..06fe0b3656 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/CustomDataSourceFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/CustomDataSourceFactory.java @@ -4,9 +4,9 @@ import androidx.annotation.Nullable; -import com.google.android.exoplayer2.upstream.ByteArrayDataSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.TransferListener; +import androidx.media3.datasource.ByteArrayDataSource; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.TransferListener; public class CustomDataSourceFactory implements DataSource.Factory { private final Context context; 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 d8d9ecb0d6..e15a73033d 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 @@ -6,19 +6,19 @@ import android.net.Uri; import android.util.Log; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.dash.manifest.DashManifest; -import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser; -import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; -import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; +import androidx.media3.common.C; +import androidx.media3.common.MediaItem; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.source.ProgressiveMediaSource; +import androidx.media3.exoplayer.dash.DashMediaSource; +import androidx.media3.exoplayer.dash.manifest.DashManifest; +import androidx.media3.exoplayer.dash.manifest.DashManifestParser; +import androidx.media3.exoplayer.hls.HlsMediaSource; +import androidx.media3.exoplayer.hls.playlist.HlsPlaylist; +import androidx.media3.exoplayer.hls.playlist.HlsPlaylistParser; +import androidx.media3.exoplayer.smoothstreaming.SsMediaSource; +import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifest; +import androidx.media3.exoplayer.smoothstreaming.manifest.SsManifestParser; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; 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 9c9324081c..ce6fef5d63 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 @@ -7,11 +7,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.MergingMediaSource; -import com.google.android.exoplayer2.source.SingleSampleMediaSource; +import androidx.media3.common.C; +import androidx.media3.common.MediaItem; +import androidx.media3.exoplayer.source.MediaSource; +import androidx.media3.exoplayer.source.MergingMediaSource; +import androidx.media3.exoplayer.source.SingleSampleMediaSource; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; @@ -30,7 +30,7 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.android.exoplayer2.C.TIME_UNSET; +import static androidx.media3.common.C.TIME_UNSET; import static org.schabi.newpipe.util.ListHelper.*; public class VideoPlaybackResolver implements PlaybackResolver { diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java index 010e1be56f..a6f43415f2 100644 --- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java +++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java @@ -5,10 +5,10 @@ import android.util.AttributeSet; import android.view.SurfaceView; -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import androidx.media3.ui.AspectRatioFrameLayout; -import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT; -import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM; +import static androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT; +import static androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM; public class ExpandableSurfaceView extends SurfaceView { private int resizeMode = RESIZE_MODE_FIT; diff --git a/app/src/main/java/us/shandian/giga/hls/manifest/HlsPlaylistParser.kt b/app/src/main/java/us/shandian/giga/hls/manifest/HlsPlaylistParser.kt index 49590671a6..5174924acb 100644 --- a/app/src/main/java/us/shandian/giga/hls/manifest/HlsPlaylistParser.kt +++ b/app/src/main/java/us/shandian/giga/hls/manifest/HlsPlaylistParser.kt @@ -1,13 +1,13 @@ package us.shandian.giga.hls.manifest import android.net.Uri -import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.Format -import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist as ExoHlsMediaPlaylist -import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment as ExoHlsSegment -import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.SegmentBase as ExoHlsSegmentBase -import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist as ExoHlsMultivariantPlaylist -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser as ExoHlsPlaylistParser +import androidx.media3.common.C +import androidx.media3.common.Format +import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist as ExoHlsMediaPlaylist +import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.Segment as ExoHlsSegment +import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.SegmentBase as ExoHlsSegmentBase +import androidx.media3.exoplayer.hls.playlist.HlsMultivariantPlaylist as ExoHlsMultivariantPlaylist +import androidx.media3.exoplayer.hls.playlist.HlsPlaylistParser as ExoHlsPlaylistParser import java.io.ByteArrayInputStream import java.io.IOException import java.net.URI diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_fastforward.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_fastforward.xml new file mode 100644 index 0000000000..4b86e109e9 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_fastforward.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_enter.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_enter.xml new file mode 100644 index 0000000000..db11f299be --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_enter.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_exit.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_exit.xml new file mode 100644 index 0000000000..782d9ff34d --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_fullscreen_exit.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_next.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_next.xml new file mode 100644 index 0000000000..6305bcbc90 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_next.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_pause.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_pause.xml new file mode 100644 index 0000000000..45cd68bed6 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_pause.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_play.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_play.xml new file mode 100644 index 0000000000..c8c4cdb127 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_play.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_previous.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_previous.xml new file mode 100644 index 0000000000..9564a2a350 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_previous.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_all.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_all.xml new file mode 100644 index 0000000000..dad37fa1f0 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_all.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_off.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_off.xml new file mode 100644 index 0000000000..132eae0d76 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_off.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_one.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_one.xml new file mode 100644 index 0000000000..d51010566a --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_repeat_one.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_rewind.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_rewind.xml new file mode 100644 index 0000000000..976b706170 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_rewind.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_off.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_off.xml new file mode 100644 index 0000000000..283ce30c3c --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_off.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_on.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_on.xml new file mode 100644 index 0000000000..123c06c43e --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_shuffle_on.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml b/app/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml new file mode 100644 index 0000000000..2e1e40cbb5 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_all.xml b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_all.xml new file mode 100644 index 0000000000..dad37fa1f0 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_all.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_off.xml b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_off.xml new file mode 100644 index 0000000000..132eae0d76 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_off.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_one.xml b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_one.xml new file mode 100644 index 0000000000..d51010566a --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v21/exo_media_action_repeat_one.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..ecf3df3cb1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..5699614c6b Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..9b8131124d Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..159bea7fd8 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_next.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..303e896187 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..f49aed7571 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_play.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..5a3e037ae9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..2c3b3af982 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..2824e7847c Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..0b92f583da Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..232aa2b1cd Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..d9e279231a Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..b693422db7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..52a805aac1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..3ad2c9c4e3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_icon_vr.png b/app/src/main/res/drawable-hdpi-v4/exo_icon_vr.png new file mode 100644 index 0000000000..90948eb44f Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_icon_vr.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..2824e7847c Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..0b92f583da Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..232aa2b1cd Binary files /dev/null and b/app/src/main/res/drawable-hdpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..e5104fcd62 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..e63921abe6 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..1af1f68a02 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..db03f46435 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_next.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..78f9bed762 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..1818039e51 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_play.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..f0b0570d0b Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..4d2eccfe9a Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..5c91a47519 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..a94abd864f Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..a59a985239 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..8cd1daa810 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..2353dd4289 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..80ec43a119 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..836f4dbb55 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_icon_vr.png b/app/src/main/res/drawable-ldpi-v4/exo_icon_vr.png new file mode 100644 index 0000000000..6e21960ed6 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_icon_vr.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..5c91a47519 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..a94abd864f Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..a59a985239 Binary files /dev/null and b/app/src/main/res/drawable-ldpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..7242e1f5c8 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..1b42a5315f Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..4423c7ce99 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..364bad0b84 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_next.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..a93aae0f34 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..3e150b5a45 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_play.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..692d8c2ad9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..ea83907d8e Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..97f7e1cc75 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..6a02321702 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..59bac33705 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..231bcee4ca Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..a55d716cce Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..0924b2cb69 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..2aeffbb6cf Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_icon_vr.png b/app/src/main/res/drawable-mdpi-v4/exo_icon_vr.png new file mode 100644 index 0000000000..02063a79a9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_icon_vr.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..97f7e1cc75 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..6a02321702 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..59bac33705 Binary files /dev/null and b/app/src/main/res/drawable-mdpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..dd31d608d4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..ab7e1fd334 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..c1dcfb2902 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..ef360fe40c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_next.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..f3552d7216 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..1c868f1831 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_play.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..381eabdccf Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..0a2ddd5e90 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..2baaedecbf Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..2468f92f9f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..4e1d53db77 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..a798fee30e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..2b67cabf5a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..ede80c9341 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..8727a93480 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_icon_vr.png b/app/src/main/res/drawable-xhdpi-v4/exo_icon_vr.png new file mode 100644 index 0000000000..ff243ef56a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_icon_vr.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..2baaedecbf Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..2468f92f9f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..4e1d53db77 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..56ed071b2f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..1e8db0ec23 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..a0a1b4d4f3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..b7f4133fd9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_next.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..131a531b37 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..ac8d4fcad5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_play.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..365b3dfee5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..884cbdd407 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..d7207ebc0d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..4d6253ead6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..d577f4ebcd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..4bab545714 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..22209d1f88 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..4c5e141a3f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..5239336671 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_icon_vr.png b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_vr.png new file mode 100644 index 0000000000..7c0338842d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_icon_vr.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..d7207ebc0d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..4d6253ead6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..d577f4ebcd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_circular_play.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_circular_play.png new file mode 100644 index 0000000000..eabf12f38a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_circular_play.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fastforward.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fastforward.png new file mode 100644 index 0000000000..cb7d89cd32 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fastforward.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_enter.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_enter.png new file mode 100644 index 0000000000..172fe0db5e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_enter.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_exit.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_exit.png new file mode 100644 index 0000000000..0db7b8f8b4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_next.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_next.png new file mode 100644 index 0000000000..7cb2bd1b5b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_next.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_pause.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_pause.png new file mode 100644 index 0000000000..c623c94267 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_pause.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_play.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_play.png new file mode 100644 index 0000000000..1f0f6818a8 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_play.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_previous.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_previous.png new file mode 100644 index 0000000000..50cf727a02 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_previous.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_all.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_all.png new file mode 100644 index 0000000000..b847d068d2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_off.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_off.png new file mode 100644 index 0000000000..64324b3fdc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_off.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_one.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_one.png new file mode 100644 index 0000000000..bd4fc75657 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_repeat_one.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_rewind.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_rewind.png new file mode 100644 index 0000000000..709611c568 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_rewind.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_off.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_off.png new file mode 100644 index 0000000000..ab42a5ad6b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_off.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_on.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_on.png new file mode 100644 index 0000000000..f6d3ac445d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_shuffle_on.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_stop.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_stop.png new file mode 100644 index 0000000000..06c99c9b59 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_icon_stop.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_all.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_all.png new file mode 100644 index 0000000000..b847d068d2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_off.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_off.png new file mode 100644 index 0000000000..64324b3fdc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_off.png differ diff --git a/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_one.png b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_one.png new file mode 100644 index 0000000000..bd4fc75657 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi-v4/exo_media_action_repeat_one.png differ diff --git a/app/src/main/res/layout-land/activity_player_queue_control.xml b/app/src/main/res/layout-land/activity_player_queue_control.xml index a055ad7acf..b77e1b6aae 100644 --- a/app/src/main/res/layout-land/activity_player_queue_control.xml +++ b/app/src/main/res/layout-land/activity_player_queue_control.xml @@ -121,7 +121,7 @@ android:clickable="true" android:focusable="true" android:scaleType="fitCenter" - android:src="@drawable/exo_controls_rewind" + android:src="@drawable/exo_icon_rewind" android:tint="?attr/colorAccent" tools:ignore="ContentDescription" /> @@ -171,7 +171,7 @@ android:clickable="true" android:focusable="true" android:scaleType="fitCenter" - android:src="@drawable/exo_controls_fastforward" + android:src="@drawable/exo_icon_fastforward" android:tint="?attr/colorAccent" tools:ignore="ContentDescription" /> diff --git a/app/src/main/res/layout/activity_player_queue_control.xml b/app/src/main/res/layout/activity_player_queue_control.xml index 2cb9922093..48c7787ccb 100644 --- a/app/src/main/res/layout/activity_player_queue_control.xml +++ b/app/src/main/res/layout/activity_player_queue_control.xml @@ -204,7 +204,7 @@ android:clickable="true" android:focusable="true" android:scaleType="fitCenter" - android:src="@drawable/exo_controls_rewind" + android:src="@drawable/exo_icon_rewind" android:tint="?attr/colorAccent" /> -