From 776dbc34f78ccab76979f3beffd4399c39cc6b4c Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Sun, 21 Jan 2018 19:32:49 -0800 Subject: [PATCH] -Added bulk playlist creation and append. -Added UI to create playlist from service player activity. -Added state saving to playlist dialogs. -Removed access to history activity on service player activity. -Made StreamEntity serializable. --- .../org/schabi/newpipe/NewPipeDatabase.java | 2 +- .../schabi/newpipe/database/Migrations.java | 2 +- .../database/stream/model/StreamEntity.java | 11 ++- .../fragments/detail/VideoDetailFragment.java | 3 +- .../fragments/local/LocalPlaylistManager.java | 48 ++++++------ .../fragments/local/PlaylistAppendDialog.java | 64 ++++++++-------- .../local/PlaylistCreationDialog.java | 44 ++--------- .../fragments/local/PlaylistDialog.java | 73 +++++++++++++++++++ .../org/schabi/newpipe/player/BasePlayer.java | 1 + .../newpipe/player/ServicePlayerActivity.java | 13 +++- .../newpipe/playlist/PlayQueueItem.java | 16 +++- app/src/main/res/menu/menu_play_queue.xml | 6 +- 12 files changed, 182 insertions(+), 101 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistDialog.java diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java index 9cd56fca4..7b33d0c10 100644 --- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java @@ -19,7 +19,7 @@ public final class NewPipeDatabase { public static void init(Context context) { databaseInstance = Room - .databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME) + .databaseBuilder(context, AppDatabase.class, DATABASE_NAME) .addMigrations(MIGRATION_11_12) .fallbackToDestructiveMigration() .build(); diff --git a/app/src/main/java/org/schabi/newpipe/database/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index 72b0d2126..9200a64b0 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -15,7 +15,7 @@ public class Migrations { /* * Unfortunately these queries must be hardcoded due to the possibility of * schema and names changing at a later date, thus invalidating the older migration - * scripts if names are not hardcoded. + * scripts if they are not hardcoded. * */ // Not much we can do about this, since room doesn't create tables before migration. diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.java b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.java index c7ef889b9..0b73e81e9 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.java @@ -9,15 +9,18 @@ import android.arch.persistence.room.PrimaryKey; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.util.Constants; +import java.io.Serializable; + import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_SERVICE_ID; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; @Entity(tableName = STREAM_TABLE, indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)}) -public class StreamEntity { +public class StreamEntity implements Serializable { final public static String STREAM_TABLE = "streams"; final public static String STREAM_ID = "uid"; @@ -78,6 +81,12 @@ public class StreamEntity { info.uploader_name, info.duration); } + @Ignore + public StreamEntity(final PlayQueueItem item) { + this(item.getServiceId(), item.getTitle(), item.getUrl(), item.getStreamType(), + item.getThumbnailUrl(), item.getUploader(), item.getDuration()); + } + @Ignore public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException { StreamInfoItem item = new StreamInfoItem( 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 7f8afdbe8..91299ac14 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 @@ -331,7 +331,8 @@ public class VideoDetailFragment extends BaseStateFragment implement break; case R.id.detail_controls_playlist_append: if (getFragmentManager() != null && currentInfo != null) { - PlaylistAppendDialog.newInstance(currentInfo).show(getFragmentManager(), TAG); + PlaylistAppendDialog.fromStreamInfo(currentInfo) + .show(getFragmentManager(), TAG); } break; case R.id.detail_uploader_root_layout: diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistManager.java index bf7bc14c8..89d69d4b4 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistManager.java @@ -33,34 +33,38 @@ public class LocalPlaylistManager { } public Maybe> createPlaylist(final String name, final List streams) { - // Disallow creation of empty playlists until user is able to select thumbnail + // Disallow creation of empty playlists if (streams.isEmpty()) return Maybe.empty(); final StreamEntity defaultStream = streams.get(0); - final PlaylistEntity newPlaylist = new PlaylistEntity(name, defaultStream.getThumbnailUrl()); + final PlaylistEntity newPlaylist = + new PlaylistEntity(name, defaultStream.getThumbnailUrl()); - return Maybe.fromCallable(() -> database.runInTransaction(() -> { - final long playlistId = playlistTable.insert(newPlaylist); - - List joinEntities = new ArrayList<>(streams.size()); - for (int index = 0; index < streams.size(); index++) { - // Upsert streams and get their ids - final long streamId = streamTable.upsert(streams.get(index)); - joinEntities.add(new PlaylistStreamEntity(playlistId, streamId, index)); - } - - return playlistStreamTable.insertAll(joinEntities); - })).subscribeOn(Schedulers.io()); + return Maybe.fromCallable(() -> database.runInTransaction(() -> + upsertStreams(playlistTable.insert(newPlaylist), streams, 0)) + ).subscribeOn(Schedulers.io()); } - public Maybe appendToPlaylist(final long playlistId, final StreamEntity stream) { - final Maybe streamIdFuture = Maybe.fromCallable(() -> streamTable.upsert(stream)); - final Maybe joinIndexFuture = - playlistStreamTable.getMaximumIndexOf(playlistId).firstElement(); + public Maybe> appendToPlaylist(final long playlistId, + final List streams) { + return playlistStreamTable.getMaximumIndexOf(playlistId) + .firstElement() + .map(maxJoinIndex -> database.runInTransaction(() -> + upsertStreams(playlistId, streams, maxJoinIndex + 1)) + ).subscribeOn(Schedulers.io()); + } - return Maybe.zip(streamIdFuture, joinIndexFuture, (streamId, currentMaxJoinIndex) -> - playlistStreamTable.insert(new PlaylistStreamEntity(playlistId, - streamId, currentMaxJoinIndex + 1)) - ).subscribeOn(Schedulers.io()); + private List upsertStreams(final long playlistId, + final List streams, + final int indexOffset) { + + List joinEntities = new ArrayList<>(streams.size()); + for (int index = 0; index < streams.size(); index++) { + // Upsert streams and get their ids + final long streamId = streamTable.upsert(streams.get(index)); + joinEntities.add(new PlaylistStreamEntity(playlistId, streamId, + index + indexOffset)); + } + return playlistStreamTable.insertAll(joinEntities); } public Completable updateJoin(final long playlistId, final List streamIds) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java index 6fad839f1..de854ae0c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java @@ -4,7 +4,6 @@ import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -19,34 +18,48 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfo; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem; +import org.schabi.newpipe.playlist.PlayQueueItem; -import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; -public class PlaylistAppendDialog extends DialogFragment { +public final class PlaylistAppendDialog extends PlaylistDialog { private static final String TAG = PlaylistAppendDialog.class.getCanonicalName(); - private static final String INFO_KEY = "info_key"; - private StreamInfo streamInfo; - - private View newPlaylistButton; private RecyclerView playlistRecyclerView; private InfoListAdapter playlistAdapter; - public static PlaylistAppendDialog newInstance(final StreamInfo info) { + public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) { PlaylistAppendDialog dialog = new PlaylistAppendDialog(); - dialog.setInfo(info); + dialog.setInfo(Collections.singletonList(new StreamEntity(info))); return dialog; } - private void setInfo(StreamInfo info) { - this.streamInfo = info; + public static PlaylistAppendDialog fromStreamInfoItems(final List items) { + PlaylistAppendDialog dialog = new PlaylistAppendDialog(); + List entities = new ArrayList<>(items.size()); + for (final StreamInfoItem item : items) { + entities.add(new StreamEntity(item)); + } + dialog.setInfo(entities); + return dialog; + } + + public static PlaylistAppendDialog fromPlayQueueItems(final List items) { + PlaylistAppendDialog dialog = new PlaylistAppendDialog(); + List entities = new ArrayList<>(items.size()); + for (final PlayQueueItem item : items) { + entities.add(new StreamEntity(item)); + } + dialog.setInfo(entities); + return dialog; } /*////////////////////////////////////////////////////////////////////////// @@ -60,14 +73,9 @@ public class PlaylistAppendDialog extends DialogFragment { playlistAdapter.useMiniItemVariants(true); } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - Serializable serial = savedInstanceState.getSerializable(INFO_KEY); - if (serial instanceof StreamInfo) streamInfo = (StreamInfo) serial; - } - } + /*////////////////////////////////////////////////////////////////////////// + // Views + //////////////////////////////////////////////////////////////////////////*/ @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, @@ -79,7 +87,7 @@ public class PlaylistAppendDialog extends DialogFragment { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - newPlaylistButton = view.findViewById(R.id.newPlaylist); + final View newPlaylistButton = view.findViewById(R.id.newPlaylist); playlistRecyclerView = view.findViewById(R.id.playlist_list); playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); playlistRecyclerView.setAdapter(playlistAdapter); @@ -92,12 +100,14 @@ public class PlaylistAppendDialog extends DialogFragment { playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { @Override public void selected(PlaylistInfoItem selectedItem) { - if (!(selectedItem instanceof LocalPlaylistInfoItem)) return; + if (!(selectedItem instanceof LocalPlaylistInfoItem) || getStreams() == null) + return; + final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId(); final Toast successToast = Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT); - playlistManager.appendToPlaylist(playlistId, new StreamEntity(streamInfo)) + playlistManager.appendToPlaylist(playlistId, getStreams()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> successToast.show()); @@ -127,20 +137,14 @@ public class PlaylistAppendDialog extends DialogFragment { }); } - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putSerializable(INFO_KEY, streamInfo); - } - /*////////////////////////////////////////////////////////////////////////// // Helper //////////////////////////////////////////////////////////////////////////*/ public void openCreatePlaylistDialog() { - if (streamInfo == null || getFragmentManager() == null) return; + if (getStreams() == null || getFragmentManager() == null) return; - PlaylistCreationDialog.newInstance(streamInfo).show(getFragmentManager(), TAG); + PlaylistCreationDialog.newInstance(getStreams()).show(getFragmentManager(), TAG); getDialog().dismiss(); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java index c43ba25b8..386ac1819 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java @@ -5,63 +5,35 @@ import android.app.Dialog; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; import android.view.View; import android.widget.EditText; import android.widget.Toast; -import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; -import org.schabi.newpipe.extractor.stream.StreamInfo; -import java.util.Collections; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; -public class PlaylistCreationDialog extends DialogFragment { +public final class PlaylistCreationDialog extends PlaylistDialog { private static final String TAG = PlaylistCreationDialog.class.getCanonicalName(); - private static final boolean DEBUG = MainActivity.DEBUG; - private static final String INFO_KEY = "info_key"; - - private StreamInfo streamInfo; - - public static PlaylistCreationDialog newInstance(final StreamInfo info) { + public static PlaylistCreationDialog newInstance(final List streams) { PlaylistCreationDialog dialog = new PlaylistCreationDialog(); - dialog.setInfo(info); + dialog.setInfo(streams); return dialog; } - private void setInfo(final StreamInfo info) { - this.streamInfo = info; - } - /*////////////////////////////////////////////////////////////////////////// - // LifeCycle + // Dialog //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (streamInfo != null) { - outState.putSerializable(INFO_KEY, streamInfo); - } - } - @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - if (savedInstanceState != null && streamInfo == null) { - final Object infoCandidate = savedInstanceState.getSerializable(INFO_KEY); - if (infoCandidate != null && infoCandidate instanceof StreamInfo) { - streamInfo = (StreamInfo) infoCandidate; - } - } - - if (streamInfo == null) return super.onCreateDialog(savedInstanceState); + if (getStreams() == null) return super.onCreateDialog(savedInstanceState); View dialogView = View.inflate(getContext(), R.layout.dialog_create_playlist, null); @@ -76,13 +48,11 @@ public class PlaylistCreationDialog extends DialogFragment { final String name = nameInput.getText().toString(); final LocalPlaylistManager playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext())); - final List streams = - Collections.singletonList(new StreamEntity(streamInfo)); final Toast successToast = Toast.makeText(getActivity(), - "Playlist " + name + " successfully created", + "Playlist successfully created", Toast.LENGTH_SHORT); - playlistManager.createPlaylist(name, streams) + playlistManager.createPlaylist(name, getStreams()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(longs -> successToast.show()); }); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistDialog.java b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistDialog.java new file mode 100644 index 000000000..010ba0181 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistDialog.java @@ -0,0 +1,73 @@ +package org.schabi.newpipe.fragments.local; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; + +import org.schabi.newpipe.database.stream.model.StreamEntity; +import org.schabi.newpipe.util.StateSaver; + +import java.util.List; +import java.util.Queue; + +public abstract class PlaylistDialog extends DialogFragment implements StateSaver.WriteRead { + + private List streamEntities; + + private StateSaver.SavedState savedState; + + protected void setInfo(final List entities) { + this.streamEntities = entities; + } + + protected List getStreams() { + return streamEntities; + } + + /*////////////////////////////////////////////////////////////////////////// + // LifeCycle + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + savedState = StateSaver.tryToRestore(savedInstanceState, this); + } + + @Override + public void onDestroy() { + super.onDestroy(); + StateSaver.onDestroy(savedState); + } + + /*////////////////////////////////////////////////////////////////////////// + // State Saving + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public String generateSuffix() { + final int size = streamEntities == null ? 0 : streamEntities.size(); + return "." + size + ".list"; + } + + @Override + public void writeTo(Queue objectsToSave) { + objectsToSave.add(streamEntities); + } + + @Override + @SuppressWarnings("unchecked") + public void readFrom(@NonNull Queue savedObjects) throws Exception { + streamEntities = (List) savedObjects.poll(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (getActivity() != null) { + savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(), + savedState, outState, this); + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 3cf169ecd..a481b3335 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -675,6 +675,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen simpleExoPlayer.seekTo(currentSourceIndex, startPos); } + // TODO: update exoplayer to 2.6.x in order to register view count on repeated streams databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe()); initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url); } diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 4165dc087..6e0f5c1d7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.Player; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; +import org.schabi.newpipe.fragments.local.PlaylistAppendDialog; import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItemBuilder; @@ -149,8 +150,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity case android.R.id.home: finish(); return true; - case R.id.action_history: - NavigationHelper.openHistory(this); + case R.id.action_append_playlist: + appendToPlaylist(); return true; case R.id.action_settings: NavigationHelper.openSettings(this); @@ -185,6 +186,14 @@ public abstract class ServicePlayerActivity extends AppCompatActivity null ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } + + private void appendToPlaylist() { + if (this.player != null && this.player.getPlayQueue() != null) { + PlaylistAppendDialog.fromPlayQueueItems(this.player.getPlayQueue().getStreams()) + .show(getSupportFragmentManager(), getTag()); + } + } + //////////////////////////////////////////////////////////////////////////// // Service Connection //////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java index 9b14e8f03..f8e7b8655 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java @@ -5,6 +5,7 @@ import android.support.annotation.Nullable; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.util.ExtractorHelper; import java.io.Serializable; @@ -23,6 +24,7 @@ public class PlayQueueItem implements Serializable { final private long duration; final private String thumbnailUrl; final private String uploader; + final private StreamType streamType; private long recoveryPosition; private Throwable error; @@ -30,22 +32,26 @@ public class PlayQueueItem implements Serializable { private transient Single stream; PlayQueueItem(@NonNull final StreamInfo info) { - this(info.getName(), info.getUrl(), info.getServiceId(), info.duration, info.thumbnail_url, info.uploader_name); + this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(), + info.getThumbnailUrl(), info.getUploaderName(), info.getStreamType()); this.stream = Single.just(info); } PlayQueueItem(@NonNull final StreamInfoItem item) { - this(item.getName(), item.getUrl(), item.getServiceId(), item.duration, item.thumbnail_url, item.uploader_name); + this(item.getName(), item.getUrl(), item.getServiceId(), item.getDuration(), + item.getThumbnailUrl(), item.getUploaderName(), item.getStreamType()); } private PlayQueueItem(final String name, final String url, final int serviceId, - final long duration, final String thumbnailUrl, final String uploader) { + final long duration, final String thumbnailUrl, final String uploader, + final StreamType streamType) { this.title = name; this.url = url; this.serviceId = serviceId; this.duration = duration; this.thumbnailUrl = thumbnailUrl; this.uploader = uploader; + this.streamType = streamType; this.recoveryPosition = RECOVERY_UNSET; } @@ -78,6 +84,10 @@ public class PlayQueueItem implements Serializable { return uploader; } + public StreamType getStreamType() { + return streamType; + } + public long getRecoveryPosition() { return recoveryPosition; } diff --git a/app/src/main/res/menu/menu_play_queue.xml b/app/src/main/res/menu/menu_play_queue.xml index 671d46329..fb64cb9fa 100644 --- a/app/src/main/res/menu/menu_play_queue.xml +++ b/app/src/main/res/menu/menu_play_queue.xml @@ -1,11 +1,11 @@ + tools:context=".player.BackgroundPlayerActivity"> -