mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-02-24 06:50:07 +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:
parent
168ac91ab8
commit
776dbc34f7
@ -19,7 +19,7 @@ public final class NewPipeDatabase {
|
|||||||
|
|
||||||
public static void init(Context context) {
|
public static void init(Context context) {
|
||||||
databaseInstance = Room
|
databaseInstance = Room
|
||||||
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
|
.databaseBuilder(context, AppDatabase.class, DATABASE_NAME)
|
||||||
.addMigrations(MIGRATION_11_12)
|
.addMigrations(MIGRATION_11_12)
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.build();
|
.build();
|
||||||
|
@ -15,7 +15,7 @@ public class Migrations {
|
|||||||
/*
|
/*
|
||||||
* Unfortunately these queries must be hardcoded due to the possibility of
|
* Unfortunately these queries must be hardcoded due to the possibility of
|
||||||
* schema and names changing at a later date, thus invalidating the older migration
|
* 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.
|
// 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.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.util.Constants;
|
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_SERVICE_ID;
|
||||||
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
|
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
|
||||||
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
|
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
|
||||||
|
|
||||||
@Entity(tableName = STREAM_TABLE,
|
@Entity(tableName = STREAM_TABLE,
|
||||||
indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)})
|
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_TABLE = "streams";
|
||||||
final public static String STREAM_ID = "uid";
|
final public static String STREAM_ID = "uid";
|
||||||
@ -78,6 +81,12 @@ public class StreamEntity {
|
|||||||
info.uploader_name, info.duration);
|
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
|
@Ignore
|
||||||
public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException {
|
public StreamInfoItem toStreamInfoItem() throws IllegalArgumentException {
|
||||||
StreamInfoItem item = new StreamInfoItem(
|
StreamInfoItem item = new StreamInfoItem(
|
||||||
|
@ -331,7 +331,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
|||||||
break;
|
break;
|
||||||
case R.id.detail_controls_playlist_append:
|
case R.id.detail_controls_playlist_append:
|
||||||
if (getFragmentManager() != null && currentInfo != null) {
|
if (getFragmentManager() != null && currentInfo != null) {
|
||||||
PlaylistAppendDialog.newInstance(currentInfo).show(getFragmentManager(), TAG);
|
PlaylistAppendDialog.fromStreamInfo(currentInfo)
|
||||||
|
.show(getFragmentManager(), TAG);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.detail_uploader_root_layout:
|
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) {
|
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();
|
if (streams.isEmpty()) return Maybe.empty();
|
||||||
final StreamEntity defaultStream = streams.get(0);
|
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(() -> {
|
return Maybe.fromCallable(() -> database.runInTransaction(() ->
|
||||||
final long playlistId = playlistTable.insert(newPlaylist);
|
upsertStreams(playlistTable.insert(newPlaylist), streams, 0))
|
||||||
|
).subscribeOn(Schedulers.io());
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return playlistStreamTable.insertAll(joinEntities);
|
|
||||||
})).subscribeOn(Schedulers.io());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Maybe<Long> appendToPlaylist(final long playlistId, final StreamEntity stream) {
|
public Maybe<List<Long>> appendToPlaylist(final long playlistId,
|
||||||
final Maybe<Long> streamIdFuture = Maybe.fromCallable(() -> streamTable.upsert(stream));
|
final List<StreamEntity> streams) {
|
||||||
final Maybe<Integer> joinIndexFuture =
|
return playlistStreamTable.getMaximumIndexOf(playlistId)
|
||||||
playlistStreamTable.getMaximumIndexOf(playlistId).firstElement();
|
.firstElement()
|
||||||
|
.map(maxJoinIndex -> database.runInTransaction(() ->
|
||||||
|
upsertStreams(playlistId, streams, maxJoinIndex + 1))
|
||||||
|
).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
return Maybe.zip(streamIdFuture, joinIndexFuture, (streamId, currentMaxJoinIndex) ->
|
private List<Long> upsertStreams(final long playlistId,
|
||||||
playlistStreamTable.insert(new PlaylistStreamEntity(playlistId,
|
final List<StreamEntity> streams,
|
||||||
streamId, currentMaxJoinIndex + 1))
|
final int indexOffset) {
|
||||||
).subscribeOn(Schedulers.io());
|
|
||||||
|
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 + indexOffset));
|
||||||
|
}
|
||||||
|
return playlistStreamTable.insertAll(joinEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Completable updateJoin(final long playlistId, final List<Long> streamIds) {
|
public Completable updateJoin(final long playlistId, final List<Long> streamIds) {
|
||||||
|
@ -4,7 +4,6 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
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.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
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.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem;
|
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.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
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 TAG = PlaylistAppendDialog.class.getCanonicalName();
|
||||||
private static final String INFO_KEY = "info_key";
|
|
||||||
|
|
||||||
private StreamInfo streamInfo;
|
|
||||||
|
|
||||||
private View newPlaylistButton;
|
|
||||||
private RecyclerView playlistRecyclerView;
|
private RecyclerView playlistRecyclerView;
|
||||||
private InfoListAdapter playlistAdapter;
|
private InfoListAdapter playlistAdapter;
|
||||||
|
|
||||||
public static PlaylistAppendDialog newInstance(final StreamInfo info) {
|
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
|
||||||
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
||||||
dialog.setInfo(info);
|
dialog.setInfo(Collections.singletonList(new StreamEntity(info)));
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInfo(StreamInfo info) {
|
public static PlaylistAppendDialog fromStreamInfoItems(final List<StreamInfoItem> items) {
|
||||||
this.streamInfo = info;
|
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);
|
playlistAdapter.useMiniItemVariants(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
// Views
|
||||||
super.onCreate(savedInstanceState);
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
if (savedInstanceState != null) {
|
|
||||||
Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
|
|
||||||
if (serial instanceof StreamInfo) streamInfo = (StreamInfo) serial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
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) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, 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 = view.findViewById(R.id.playlist_list);
|
||||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
playlistRecyclerView.setAdapter(playlistAdapter);
|
playlistRecyclerView.setAdapter(playlistAdapter);
|
||||||
@ -92,12 +100,14 @@ public class PlaylistAppendDialog extends DialogFragment {
|
|||||||
playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
||||||
@Override
|
@Override
|
||||||
public void selected(PlaylistInfoItem selectedItem) {
|
public void selected(PlaylistInfoItem selectedItem) {
|
||||||
if (!(selectedItem instanceof LocalPlaylistInfoItem)) return;
|
if (!(selectedItem instanceof LocalPlaylistInfoItem) || getStreams() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId();
|
final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId();
|
||||||
final Toast successToast =
|
final Toast successToast =
|
||||||
Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT);
|
Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT);
|
||||||
|
|
||||||
playlistManager.appendToPlaylist(playlistId, new StreamEntity(streamInfo))
|
playlistManager.appendToPlaylist(playlistId, getStreams())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(ignored -> successToast.show());
|
.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
|
// Helper
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public void openCreatePlaylistDialog() {
|
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();
|
getDialog().dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,63 +5,35 @@ import android.app.Dialog;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
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 String TAG = PlaylistCreationDialog.class.getCanonicalName();
|
||||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
|
||||||
|
|
||||||
private static final String INFO_KEY = "info_key";
|
public static PlaylistCreationDialog newInstance(final List<StreamEntity> streams) {
|
||||||
|
|
||||||
private StreamInfo streamInfo;
|
|
||||||
|
|
||||||
public static PlaylistCreationDialog newInstance(final StreamInfo info) {
|
|
||||||
PlaylistCreationDialog dialog = new PlaylistCreationDialog();
|
PlaylistCreationDialog dialog = new PlaylistCreationDialog();
|
||||||
dialog.setInfo(info);
|
dialog.setInfo(streams);
|
||||||
return dialog;
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
if (savedInstanceState != null && streamInfo == null) {
|
if (getStreams() == null) return super.onCreateDialog(savedInstanceState);
|
||||||
final Object infoCandidate = savedInstanceState.getSerializable(INFO_KEY);
|
|
||||||
if (infoCandidate != null && infoCandidate instanceof StreamInfo) {
|
|
||||||
streamInfo = (StreamInfo) infoCandidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streamInfo == null) return super.onCreateDialog(savedInstanceState);
|
|
||||||
|
|
||||||
View dialogView = View.inflate(getContext(),
|
View dialogView = View.inflate(getContext(),
|
||||||
R.layout.dialog_create_playlist, null);
|
R.layout.dialog_create_playlist, null);
|
||||||
@ -76,13 +48,11 @@ public class PlaylistCreationDialog extends DialogFragment {
|
|||||||
final String name = nameInput.getText().toString();
|
final String name = nameInput.getText().toString();
|
||||||
final LocalPlaylistManager playlistManager =
|
final LocalPlaylistManager playlistManager =
|
||||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||||
final List<StreamEntity> streams =
|
|
||||||
Collections.singletonList(new StreamEntity(streamInfo));
|
|
||||||
final Toast successToast = Toast.makeText(getActivity(),
|
final Toast successToast = Toast.makeText(getActivity(),
|
||||||
"Playlist " + name + " successfully created",
|
"Playlist successfully created",
|
||||||
Toast.LENGTH_SHORT);
|
Toast.LENGTH_SHORT);
|
||||||
|
|
||||||
playlistManager.createPlaylist(name, streams)
|
playlistManager.createPlaylist(name, getStreams())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(longs -> successToast.show());
|
.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);
|
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());
|
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe());
|
||||||
initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url);
|
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.R;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
|
import org.schabi.newpipe.fragments.local.PlaylistAppendDialog;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||||
@ -149,8 +150,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
case android.R.id.home:
|
case android.R.id.home:
|
||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_history:
|
case R.id.action_append_playlist:
|
||||||
NavigationHelper.openHistory(this);
|
appendToPlaylist();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_settings:
|
case R.id.action_settings:
|
||||||
NavigationHelper.openSettings(this);
|
NavigationHelper.openSettings(this);
|
||||||
@ -185,6 +186,14 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
null
|
null
|
||||||
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
).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
|
// Service Connection
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -5,6 +5,7 @@ import android.support.annotation.Nullable;
|
|||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -23,6 +24,7 @@ public class PlayQueueItem implements Serializable {
|
|||||||
final private long duration;
|
final private long duration;
|
||||||
final private String thumbnailUrl;
|
final private String thumbnailUrl;
|
||||||
final private String uploader;
|
final private String uploader;
|
||||||
|
final private StreamType streamType;
|
||||||
|
|
||||||
private long recoveryPosition;
|
private long recoveryPosition;
|
||||||
private Throwable error;
|
private Throwable error;
|
||||||
@ -30,22 +32,26 @@ public class PlayQueueItem implements Serializable {
|
|||||||
private transient Single<StreamInfo> stream;
|
private transient Single<StreamInfo> stream;
|
||||||
|
|
||||||
PlayQueueItem(@NonNull final StreamInfo info) {
|
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);
|
this.stream = Single.just(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayQueueItem(@NonNull final StreamInfoItem item) {
|
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,
|
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.title = name;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.serviceId = serviceId;
|
this.serviceId = serviceId;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.thumbnailUrl = thumbnailUrl;
|
this.thumbnailUrl = thumbnailUrl;
|
||||||
this.uploader = uploader;
|
this.uploader = uploader;
|
||||||
|
this.streamType = streamType;
|
||||||
|
|
||||||
this.recoveryPosition = RECOVERY_UNSET;
|
this.recoveryPosition = RECOVERY_UNSET;
|
||||||
}
|
}
|
||||||
@ -78,6 +84,10 @@ public class PlayQueueItem implements Serializable {
|
|||||||
return uploader;
|
return uploader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StreamType getStreamType() {
|
||||||
|
return streamType;
|
||||||
|
}
|
||||||
|
|
||||||
public long getRecoveryPosition() {
|
public long getRecoveryPosition() {
|
||||||
return recoveryPosition;
|
return recoveryPosition;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
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:orderInCategory="981"
|
||||||
android:title="@string/action_history"
|
android:title="@string/append_playlist"
|
||||||
app:showAsAction="never"/>
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
<item android:id="@+id/action_settings"
|
<item android:id="@+id/action_settings"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user