mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 06:43:00 +00:00 
			
		
		
		
	-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.
This commit is contained in:
		| @@ -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(); | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -331,7 +331,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> 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: | ||||
|   | ||||
| @@ -33,34 +33,38 @@ public class LocalPlaylistManager { | ||||
|     } | ||||
|  | ||||
|     public Maybe<List<Long>> createPlaylist(final String name, final List<StreamEntity> 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); | ||||
|         return Maybe.fromCallable(() -> database.runInTransaction(() -> | ||||
|                 upsertStreams(playlistTable.insert(newPlaylist), streams, 0)) | ||||
|         ).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|  | ||||
|     public Maybe<List<Long>> appendToPlaylist(final long playlistId, | ||||
|                                               final List<StreamEntity> streams) { | ||||
|         return playlistStreamTable.getMaximumIndexOf(playlistId) | ||||
|                 .firstElement() | ||||
|                 .map(maxJoinIndex -> database.runInTransaction(() -> | ||||
|                         upsertStreams(playlistId, streams, maxJoinIndex + 1)) | ||||
|                 ).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|  | ||||
|     private List<Long> upsertStreams(final long playlistId, | ||||
|                                      final List<StreamEntity> streams, | ||||
|                                      final int indexOffset) { | ||||
|  | ||||
|         List<PlaylistStreamEntity> 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)); | ||||
|             joinEntities.add(new PlaylistStreamEntity(playlistId, streamId, | ||||
|                     index + indexOffset)); | ||||
|         } | ||||
|  | ||||
|         return playlistStreamTable.insertAll(joinEntities); | ||||
|         })).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|  | ||||
|     public Maybe<Long> appendToPlaylist(final long playlistId, final StreamEntity stream) { | ||||
|         final Maybe<Long> streamIdFuture = Maybe.fromCallable(() -> streamTable.upsert(stream)); | ||||
|         final Maybe<Integer> joinIndexFuture = | ||||
|                 playlistStreamTable.getMaximumIndexOf(playlistId).firstElement(); | ||||
|  | ||||
|         return Maybe.zip(streamIdFuture, joinIndexFuture, (streamId, currentMaxJoinIndex) -> | ||||
|                 playlistStreamTable.insert(new PlaylistStreamEntity(playlistId, | ||||
|                         streamId, currentMaxJoinIndex + 1)) | ||||
|         ).subscribeOn(Schedulers.io()); | ||||
|     } | ||||
|  | ||||
|     public Completable updateJoin(final long playlistId, final List<Long> streamIds) { | ||||
|   | ||||
| @@ -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<StreamInfoItem> items) { | ||||
|         PlaylistAppendDialog dialog = new PlaylistAppendDialog(); | ||||
|         List<StreamEntity> 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<PlayQueueItem> items) { | ||||
|         PlaylistAppendDialog dialog = new PlaylistAppendDialog(); | ||||
|         List<StreamEntity> 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<PlaylistInfoItem>() { | ||||
|             @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(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<StreamEntity> 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<StreamEntity> 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()); | ||||
|                 }); | ||||
|   | ||||
| @@ -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<StreamEntity> streamEntities; | ||||
|  | ||||
|     private StateSaver.SavedState savedState; | ||||
|  | ||||
|     protected void setInfo(final List<StreamEntity> entities) { | ||||
|         this.streamEntities = entities; | ||||
|     } | ||||
|  | ||||
|     protected List<StreamEntity> 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<Object> objectsToSave) { | ||||
|         objectsToSave.add(streamEntities); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception { | ||||
|         streamEntities = (List<StreamEntity>) savedObjects.poll(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onSaveInstanceState(Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
|         if (getActivity() != null) { | ||||
|             savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(), | ||||
|                     savedState, outState, this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|   | ||||
| @@ -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<StreamInfo> 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; | ||||
|     } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     tools:context="org.schabi.newpipe.history.HistoryActivity"> | ||||
|     tools:context=".player.BackgroundPlayerActivity"> | ||||
|  | ||||
|     <item android:id="@+id/action_history" | ||||
|     <item android:id="@+id/action_append_playlist" | ||||
|         android:orderInCategory="981" | ||||
|         android:title="@string/action_history" | ||||
|         android:title="@string/append_playlist" | ||||
|         app:showAsAction="never"/> | ||||
|  | ||||
|     <item android:id="@+id/action_settings" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 John Zhen Mo
					John Zhen Mo