mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2024-11-22 19:12:45 +01:00
-Added UI for creating playlist.
-Added UI for appending item to playlists. -Added mini variant of playlist info item.
This commit is contained in:
parent
f71242a036
commit
38946e4b0f
@ -48,7 +48,6 @@ public abstract class StreamHistoryDAO implements BasicDAO<StreamHistoryEntity>
|
||||
" COUNT(*) AS " + STREAM_WATCH_COUNT +
|
||||
" FROM " + STREAM_HISTORY_TABLE + " GROUP BY " + JOIN_STREAM_ID + ")" +
|
||||
|
||||
" ON " + STREAM_ID + " = " + JOIN_STREAM_ID +
|
||||
" ORDER BY " + STREAM_ACCESS_DATE + " DESC")
|
||||
" ON " + STREAM_ID + " = " + JOIN_STREAM_ID)
|
||||
public abstract Flowable<List<StreamStatisticsEntry>> getStatistics();
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class LocalPlaylistManager {
|
||||
|
||||
@ -46,7 +48,7 @@ public class LocalPlaylistManager {
|
||||
}
|
||||
|
||||
return playlistStreamTable.insertAll(joinEntities);
|
||||
}));
|
||||
})).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Maybe<Long> appendToPlaylist(final long playlistId, final StreamEntity stream) {
|
||||
@ -57,7 +59,7 @@ public class LocalPlaylistManager {
|
||||
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) {
|
||||
@ -73,6 +75,8 @@ public class LocalPlaylistManager {
|
||||
}
|
||||
|
||||
public Maybe<List<PlaylistMetadataEntry>> getPlaylists() {
|
||||
return playlistStreamTable.getPlaylistMetadata().firstElement();
|
||||
return playlistStreamTable.getPlaylistMetadata()
|
||||
.firstElement()
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,147 @@
|
||||
package org.schabi.newpipe.fragments.playlist;
|
||||
|
||||
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;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
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.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
||||
public class PlaylistAppendDialog extends DialogFragment {
|
||||
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) {
|
||||
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
||||
dialog.setInfo(info);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void setInfo(StreamInfo info) {
|
||||
this.streamInfo = info;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
playlistAdapter = new InfoListAdapter(getActivity());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.dialog_playlists, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||
playlistRecyclerView = view.findViewById(R.id.playlist_list);
|
||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
playlistRecyclerView.setAdapter(playlistAdapter);
|
||||
|
||||
final LocalPlaylistManager playlistManager =
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
|
||||
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
||||
|
||||
playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
||||
@Override
|
||||
public void selected(PlaylistInfoItem selectedItem) {
|
||||
if (!(selectedItem instanceof LocalPlaylistInfoItem)) return;
|
||||
final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId();
|
||||
final Toast successToast =
|
||||
Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT);
|
||||
|
||||
playlistManager.appendToPlaylist(playlistId, new StreamEntity(streamInfo))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> successToast.show());
|
||||
|
||||
getDialog().dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(PlaylistInfoItem selectedItem) {}
|
||||
});
|
||||
|
||||
playlistManager.getPlaylists()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(metadataEntries -> {
|
||||
if (metadataEntries.isEmpty()) {
|
||||
openCreatePlaylistDialog();
|
||||
}
|
||||
|
||||
List<InfoItem> playlistInfoItems = new ArrayList<>(metadataEntries.size());
|
||||
for (final PlaylistMetadataEntry metadataEntry : metadataEntries) {
|
||||
playlistInfoItems.add(metadataEntry.toStoredPlaylistInfoItem());
|
||||
}
|
||||
|
||||
playlistAdapter.clearStreamItemList();
|
||||
playlistAdapter.addInfoItemList(playlistInfoItems);
|
||||
playlistRecyclerView.setVisibility(View.VISIBLE);
|
||||
|
||||
getDialog().setCanceledOnTouchOutside(true);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable(INFO_KEY, streamInfo);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Helper
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void openCreatePlaylistDialog() {
|
||||
if (streamInfo == null || getFragmentManager() == null) return;
|
||||
|
||||
getDialog().dismiss();
|
||||
PlaylistCreationDialog.newInstance(streamInfo).show(getFragmentManager(), TAG);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package org.schabi.newpipe.fragments.playlist;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
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 {
|
||||
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) {
|
||||
PlaylistCreationDialog dialog = new PlaylistCreationDialog();
|
||||
dialog.setInfo(info);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void setInfo(final StreamInfo info) {
|
||||
this.streamInfo = info;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@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);
|
||||
|
||||
View dialogView = View.inflate(getContext(),
|
||||
R.layout.dialog_create_playlist, null);
|
||||
EditText nameInput = dialogView.findViewById(R.id.playlist_name);
|
||||
|
||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.create_playlist)
|
||||
.setView(dialogView)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.create, (dialogInterface, i) -> {
|
||||
final String name = nameInput.getText().toString();
|
||||
final LocalPlaylistManager playlistManager =
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
final List<StreamEntity> streams =
|
||||
Collections.singletonList(new StreamEntity(streamInfo));
|
||||
|
||||
playlistManager.createPlaylist(name, streams)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(longs -> Toast.makeText(getActivity(),
|
||||
"Playlist " + name + " successfully created",
|
||||
Toast.LENGTH_SHORT).show());
|
||||
});
|
||||
|
||||
return dialogBuilder.create();
|
||||
}
|
||||
}
|
@ -44,4 +44,31 @@ public class StreamRecordManager {
|
||||
public int removeHistory(final long streamId) {
|
||||
return historyTable.deleteHistory(streamId);
|
||||
}
|
||||
|
||||
public void removeRecord() {
|
||||
historyTable.getStatistics().firstElement().subscribe(
|
||||
new MaybeObserver<List<StreamStatisticsEntry>>() {
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<StreamStatisticsEntry> streamStatisticsEntries) {
|
||||
hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
|
||||
|
||||
@ -75,7 +76,7 @@ public class InfoItemBuilder {
|
||||
case CHANNEL:
|
||||
return useMiniVariant ? new ChannelMiniInfoItemHolder(this, parent) : new ChannelInfoItemHolder(this, parent);
|
||||
case PLAYLIST:
|
||||
return new PlaylistInfoItemHolder(this, parent);
|
||||
return useMiniVariant ? new PlaylistMiniInfoItemHolder(this, parent) : new PlaylistInfoItemHolder(this, parent);
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
throw new RuntimeException("InfoType not expected = " + infoType.name());
|
||||
|
@ -19,7 +19,7 @@ public class InfoItemDialog {
|
||||
@NonNull final StreamInfoItem info,
|
||||
@NonNull final String[] commands,
|
||||
@NonNull final DialogInterface.OnClickListener actions) {
|
||||
this(activity, commands, actions, info.getName(), info.uploader_name);
|
||||
this(activity, commands, actions, info.getName(), info.getUploaderName());
|
||||
}
|
||||
|
||||
public InfoItemDialog(@NonNull final Activity activity,
|
||||
@ -28,8 +28,7 @@ public class InfoItemDialog {
|
||||
@NonNull final String title,
|
||||
@Nullable final String additionalDetail) {
|
||||
|
||||
final LayoutInflater inflater = activity.getLayoutInflater();
|
||||
final View bannerView = inflater.inflate(R.layout.dialog_title, null);
|
||||
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
|
||||
bannerView.setSelected(true);
|
||||
|
||||
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
||||
|
@ -15,6 +15,7 @@ import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
|
||||
|
||||
@ -52,6 +53,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
private static final int STREAM_HOLDER_TYPE = 0x101;
|
||||
private static final int MINI_CHANNEL_HOLDER_TYPE = 0x200;
|
||||
private static final int CHANNEL_HOLDER_TYPE = 0x201;
|
||||
private static final int MINI_PLAYLIST_HOLDER_TYPE = 0x300;
|
||||
private static final int PLAYLIST_HOLDER_TYPE = 0x301;
|
||||
|
||||
private final InfoItemBuilder infoItemBuilder;
|
||||
@ -207,7 +209,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
case CHANNEL:
|
||||
return useMiniVariant ? MINI_CHANNEL_HOLDER_TYPE : CHANNEL_HOLDER_TYPE;
|
||||
case PLAYLIST:
|
||||
return PLAYLIST_HOLDER_TYPE;
|
||||
return useMiniVariant ? MINI_PLAYLIST_HOLDER_TYPE : PLAYLIST_HOLDER_TYPE;
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
return -1;
|
||||
@ -230,6 +232,8 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
return new ChannelMiniInfoItemHolder(infoItemBuilder, parent);
|
||||
case CHANNEL_HOLDER_TYPE:
|
||||
return new ChannelInfoItemHolder(infoItemBuilder, parent);
|
||||
case MINI_PLAYLIST_HOLDER_TYPE:
|
||||
return new PlaylistMiniInfoItemHolder(infoItemBuilder, parent);
|
||||
case PLAYLIST_HOLDER_TYPE:
|
||||
return new PlaylistInfoItemHolder(infoItemBuilder, parent);
|
||||
default:
|
||||
|
@ -1,62 +1,13 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
||||
public class PlaylistInfoItemHolder extends InfoItemHolder {
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemStreamCountView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
public class PlaylistInfoItemHolder extends PlaylistMiniInfoItemHolder {
|
||||
|
||||
public PlaylistInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
super(infoItemBuilder, R.layout.list_playlist_item, parent);
|
||||
|
||||
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
|
||||
itemTitleView = itemView.findViewById(R.id.itemTitleView);
|
||||
itemStreamCountView = itemView.findViewById(R.id.itemStreamCountView);
|
||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
||||
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
||||
|
||||
itemTitleView.setText(item.getName());
|
||||
itemStreamCountView.setText(item.stream_count + "");
|
||||
itemUploaderView.setText(item.uploader_name);
|
||||
|
||||
itemBuilder.getImageLoader()
|
||||
.displayImage(item.thumbnail_url, itemThumbnailView, DISPLAY_THUMBNAIL_OPTIONS);
|
||||
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (itemBuilder.getOnPlaylistSelectedListener() != null) {
|
||||
itemBuilder.getOnPlaylistSelectedListener().selected(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display options for playlist thumbnails
|
||||
*/
|
||||
public static final DisplayImageOptions DISPLAY_THUMBNAIL_OPTIONS =
|
||||
new DisplayImageOptions.Builder()
|
||||
.cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS)
|
||||
.showImageOnLoading(R.drawable.dummy_thumbnail_playlist)
|
||||
.showImageForEmptyUri(R.drawable.dummy_thumbnail_playlist)
|
||||
.showImageOnFail(R.drawable.dummy_thumbnail_playlist)
|
||||
.build();
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
||||
public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemStreamCountView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
|
||||
public PlaylistMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||
super(infoItemBuilder, layoutId, parent);
|
||||
|
||||
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
|
||||
itemTitleView = itemView.findViewById(R.id.itemTitleView);
|
||||
itemStreamCountView = itemView.findViewById(R.id.itemStreamCountView);
|
||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||
}
|
||||
|
||||
public PlaylistMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
this(infoItemBuilder, R.layout.list_playlist_mini_item, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
||||
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
||||
|
||||
itemTitleView.setText(item.getName());
|
||||
itemStreamCountView.setText(String.valueOf(item.getStreamCount()));
|
||||
itemUploaderView.setText(item.getUploaderName());
|
||||
|
||||
itemBuilder.getImageLoader()
|
||||
.displayImage(item.thumbnail_url, itemThumbnailView, DISPLAY_THUMBNAIL_OPTIONS);
|
||||
|
||||
itemView.setOnClickListener(view -> {
|
||||
if (itemBuilder.getOnPlaylistSelectedListener() != null) {
|
||||
itemBuilder.getOnPlaylistSelectedListener().selected(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display options for playlist thumbnails
|
||||
*/
|
||||
public static final DisplayImageOptions DISPLAY_THUMBNAIL_OPTIONS =
|
||||
new DisplayImageOptions.Builder()
|
||||
.cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS)
|
||||
.showImageOnLoading(R.drawable.dummy_thumbnail_playlist)
|
||||
.showImageForEmptyUri(R.drawable.dummy_thumbnail_playlist)
|
||||
.showImageOnFail(R.drawable.dummy_thumbnail_playlist)
|
||||
.build();
|
||||
}
|
@ -1,30 +1,20 @@
|
||||
package org.schabi.newpipe.info_list.stored;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
|
||||
import static org.schabi.newpipe.util.Constants.NO_SERVICE_ID;
|
||||
import static org.schabi.newpipe.util.Constants.NO_URL;
|
||||
|
||||
public class LocalPlaylistInfoItem extends InfoItem {
|
||||
public class LocalPlaylistInfoItem extends PlaylistInfoItem {
|
||||
private final long playlistId;
|
||||
private long streamCount;
|
||||
|
||||
public LocalPlaylistInfoItem(final long playlistId, final String name) {
|
||||
super(InfoType.PLAYLIST, NO_SERVICE_ID, NO_URL, name);
|
||||
super(NO_SERVICE_ID, NO_URL, name);
|
||||
|
||||
this.playlistId = playlistId;
|
||||
this.streamCount = streamCount;
|
||||
}
|
||||
|
||||
public long getPlaylistId() {
|
||||
return playlistId;
|
||||
}
|
||||
|
||||
public long getStreamCount() {
|
||||
return streamCount;
|
||||
}
|
||||
|
||||
public void setStreamCount(long streamCount) {
|
||||
this.streamCount = streamCount;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
package org.schabi.newpipe.info_list.stored;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class StreamStatisticsInfoItem extends InfoItem {
|
||||
public class StreamStatisticsInfoItem extends StreamInfoItem {
|
||||
private final long streamId;
|
||||
|
||||
private Date latestAccessDate;
|
||||
private long watchCount;
|
||||
|
||||
public StreamStatisticsInfoItem(final long streamId, final int serviceId,
|
||||
final String url, final String name) {
|
||||
super(InfoType.STREAM, serviceId, url, name);
|
||||
final String url, final String name, final StreamType type) {
|
||||
super(serviceId, url, name, type);
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,6 @@ import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
||||
|
||||
|
22
app/src/main/res/layout/dialog_create_playlist.xml
Normal file
22
app/src/main/res/layout/dialog_create_playlist.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/itemRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:paddingLeft="@dimen/video_item_search_padding"
|
||||
android:paddingRight="@dimen/video_item_search_padding"
|
||||
android:paddingTop="@dimen/video_item_search_padding">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/playlist_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:saveEnabled="true"
|
||||
android:inputType="text"
|
||||
android:hint="@string/playlist_name_input"
|
||||
android:maxLines="1"/>
|
||||
</RelativeLayout>
|
57
app/src/main/res/layout/dialog_playlists.xml
Normal file
57
app/src/main/res/layout/dialog_playlists.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/newPlaylist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
<ImageView
|
||||
android:id="@+id/newPlaylistIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:src="?attr/palette"
|
||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/newPlaylistText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_toRightOf="@+id/newPlaylistIcon"
|
||||
android:gravity="left|center"
|
||||
android:text="Create New Playlist"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@+id/newPlaylist"
|
||||
android:layout_marginLeft="@dimen/video_item_search_padding"
|
||||
android:layout_marginRight="@dimen/video_item_search_padding"
|
||||
android:background="?attr/separator_color"/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/playlist_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/separator"
|
||||
android:background="?android:windowBackground"
|
||||
android:scrollbars="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:listitem="@layout/list_playlist_mini_item"/>
|
||||
</RelativeLayout>
|
70
app/src/main/res/layout/list_playlist_mini_item.xml
Normal file
70
app/src/main/res/layout/list_playlist_mini_item.xml
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:scaleType="fitEnd"
|
||||
android:src="@drawable/dummy_thumbnail_playlist"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemStreamCountView"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@id/itemThumbnailView"
|
||||
android:layout_alignRight="@id/itemThumbnailView"
|
||||
android:layout_alignTop="@id/itemThumbnailView"
|
||||
android:background="@color/playlist_stream_count_background_color"
|
||||
android:drawableTop="@drawable/ic_playlist_play_white_24dp"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingTop="4dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/duration_text_color"
|
||||
android:textSize="@dimen/video_item_search_duration_text_size"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="3141"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitleView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemUploaderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemTitleView"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="Uploader"/>
|
||||
|
||||
</RelativeLayout>
|
@ -224,6 +224,7 @@
|
||||
<string name="start">Start</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="view">Play</string>
|
||||
<string name="create">Create</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="checksum">Checksum</string>
|
||||
|
||||
@ -353,6 +354,7 @@
|
||||
<string name="youtube" translatable="false">YouTube</string>
|
||||
<string name="soundcloud" translatable="false">SoundCloud</string>
|
||||
|
||||
|
||||
<!-- Preferred player -->
|
||||
<string name="preferred_player_share_menu_title" translatable="false">@string/preferred_player_settings_title</string>
|
||||
<string name="preferred_player_share_menu_dialog_title">Open with preferred player</string>
|
||||
@ -365,4 +367,8 @@
|
||||
|
||||
<string name="preferred_player_fetcher_notification_title">Getting info…</string>
|
||||
<string name="preferred_player_fetcher_notification_message">"The requested content is loading"</string>
|
||||
|
||||
<!-- Local Playlist -->
|
||||
<string name="create_playlist">Create New Playlist</string>
|
||||
<string name="playlist_name_input">Name</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user