diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamHistoryEntry.java b/app/src/main/java/org/schabi/newpipe/database/stream/StreamHistoryEntry.java new file mode 100644 index 000000000..3df641372 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamHistoryEntry.java @@ -0,0 +1,47 @@ +package org.schabi.newpipe.database.stream; + +import android.arch.persistence.room.ColumnInfo; + +import org.schabi.newpipe.database.stream.model.StreamEntity; +import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; +import org.schabi.newpipe.extractor.stream.StreamType; + +import java.util.Date; + +public class StreamHistoryEntry { + @ColumnInfo(name = StreamEntity.STREAM_ID) + final public long uid; + @ColumnInfo(name = StreamEntity.STREAM_SERVICE_ID) + final public int serviceId; + @ColumnInfo(name = StreamEntity.STREAM_URL) + final public String url; + @ColumnInfo(name = StreamEntity.STREAM_TITLE) + final public String title; + @ColumnInfo(name = StreamEntity.STREAM_TYPE) + final public StreamType streamType; + @ColumnInfo(name = StreamEntity.STREAM_DURATION) + final public long duration; + @ColumnInfo(name = StreamEntity.STREAM_UPLOADER) + final public String uploader; + @ColumnInfo(name = StreamEntity.STREAM_THUMBNAIL_URL) + final public String thumbnailUrl; + @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) + final public long streamId; + @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) + final public Date accessDate; + + public StreamHistoryEntry(long uid, int serviceId, String url, String title, + StreamType streamType, long duration, String uploader, + String thumbnailUrl, long streamId, Date accessDate) { + this.uid = uid; + this.serviceId = serviceId; + this.url = url; + this.title = title; + this.streamType = streamType; + this.duration = duration; + this.uploader = uploader; + this.thumbnailUrl = thumbnailUrl; + this.streamId = streamId; + this.accessDate = accessDate; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.java index ee246db1a..a4955d835 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.java @@ -7,17 +7,23 @@ import android.arch.persistence.room.Query; import android.arch.persistence.room.Transaction; import org.schabi.newpipe.database.BasicDAO; +import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity; +import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; +import org.schabi.newpipe.database.stream.model.StreamStateEntity; import java.util.ArrayList; import java.util.List; import io.reactivex.Flowable; +import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_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_URL; +import static org.schabi.newpipe.database.stream.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; +import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; @Dao public abstract class StreamDAO implements BasicDAO { @@ -77,4 +83,22 @@ public abstract class StreamDAO implements BasicDAO { update(streams); return streamIds; } + + @Query("DELETE FROM " + STREAM_TABLE + " WHERE " + STREAM_ID + + " NOT IN " + + "(SELECT DISTINCT " + STREAM_ID + " FROM " + STREAM_TABLE + + + " LEFT JOIN " + STREAM_HISTORY_TABLE + + " ON " + STREAM_ID + " = " + + StreamHistoryEntity.STREAM_HISTORY_TABLE + "." + StreamHistoryEntity.JOIN_STREAM_ID + + + " LEFT JOIN " + STREAM_STATE_TABLE + + " ON " + STREAM_ID + " = " + + StreamStateEntity.STREAM_STATE_TABLE + "." + StreamStateEntity.JOIN_STREAM_ID + + + " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE + + " ON " + STREAM_ID + " = " + + PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE + "." + PlaylistStreamEntity.JOIN_STREAM_ID + + ")") + public abstract int deleteOrphans(); } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamHistoryDAO.java index 527d151ea..81ee9d912 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamHistoryDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamHistoryDAO.java @@ -6,6 +6,7 @@ import android.arch.persistence.room.Query; import android.arch.persistence.room.Transaction; import org.schabi.newpipe.database.BasicDAO; +import org.schabi.newpipe.database.stream.StreamHistoryEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.model.StreamHistoryEntity; @@ -36,6 +37,12 @@ public abstract class StreamHistoryDAO implements BasicDAO throw new UnsupportedOperationException(); } + @Query("SELECT * FROM " + STREAM_TABLE + + " INNER JOIN " + STREAM_HISTORY_TABLE + + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID + + " ORDER BY " + STREAM_ACCESS_DATE + " DESC") + public abstract Flowable> getHistory(); + @Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId") public abstract int deleteStreamHistory(final long streamId); 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 91299ac14..05550a0a5 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 @@ -62,6 +62,7 @@ import org.schabi.newpipe.fragments.local.PlaylistAppendDialog; import org.schabi.newpipe.history.HistoryListener; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.player.helper.PlayerHelper; @@ -471,7 +472,7 @@ public class VideoDetailFragment extends BaseStateFragment implement @Override protected void initListeners() { super.initListeners(); - infoItemBuilder.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoItemBuilder.setOnStreamSelectedListener(new OnInfoItemGesture() { @Override public void selected(StreamInfoItem selectedItem) { selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index a09a472a5..9e4fe89ab 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -3,19 +3,15 @@ package org.schabi.newpipe.fragments.list; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; -import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.View; -import android.widget.TextView; -import android.widget.Toast; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; @@ -24,12 +20,11 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.StateSaver; import java.util.List; @@ -140,7 +135,7 @@ public abstract class BaseListFragment extends BaseStateFragment implem @Override protected void initListeners() { super.initListeners(); - infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture() { @Override public void selected(StreamInfoItem selectedItem) { onItemSelected(selectedItem); @@ -155,7 +150,7 @@ public abstract class BaseListFragment extends BaseStateFragment implem } }); - infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoListAdapter.setOnChannelSelectedListener(new OnInfoItemGesture() { @Override public void selected(ChannelInfoItem selectedItem) { onItemSelected(selectedItem); @@ -163,12 +158,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); } - - @Override - public void held(ChannelInfoItem selectedItem) {} }); - infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoListAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture() { @Override public void selected(PlaylistInfoItem selectedItem) { onItemSelected(selectedItem); @@ -176,9 +168,6 @@ public abstract class BaseListFragment extends BaseStateFragment implem useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); } - - @Override - public void held(PlaylistInfoItem selectedItem) {} }); itemsList.clearOnScrollListeners(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/local/BookmarkFragment.java index ecbd416ee..769365dd8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/BookmarkFragment.java @@ -21,9 +21,9 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; @@ -33,10 +33,8 @@ import java.util.Collections; import java.util.List; import icepick.State; -import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; import static org.schabi.newpipe.util.AnimationUtils.animateView; @@ -143,7 +141,7 @@ public class BookmarkFragment extends BaseStateFragment() { + infoListAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture() { @Override public void selected(PlaylistInfoItem selectedItem) { // Requires the parent fragment to find holder for fragment replacement diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistFragment.java index 802532272..7ba5db7e1 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalPlaylistFragment.java @@ -21,8 +21,8 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.playlist.PlayQueue; import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.report.UserAction; @@ -141,7 +141,7 @@ public class LocalPlaylistFragment extends BaseListFragment, protected void initListeners() { super.initListeners(); - infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture() { @Override public void selected(StreamInfoItem selectedItem) { // Requires the parent fragment to find holder for fragment replacement @@ -219,7 +219,7 @@ public class LocalPlaylistFragment extends BaseListFragment, super.startLoading(forceLoad); resetFragment(); - playlistManager.getPlaylist(playlistId) + playlistManager.getPlaylistStreams(playlistId) .observeOn(AndroidSchedulers.mainThread()) .subscribe(getPlaylistObserver()); } @@ -317,7 +317,7 @@ public class LocalPlaylistFragment extends BaseListFragment, if (super.onError(exception)) return true; onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, - "none", "Subscriptions", R.string.general_error); + "none", "Local Playlist", R.string.general_error); return true; } 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 5633e104d..4bc161c04 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 @@ -1,5 +1,7 @@ package org.schabi.newpipe.fragments.local; +import android.support.annotation.Nullable; + import org.schabi.newpipe.database.AppDatabase; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.dao.PlaylistDAO; @@ -82,7 +84,7 @@ public class LocalPlaylistManager { return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io()); } - public Flowable> getPlaylist(final long playlistId) { + public Flowable> getPlaylistStreams(final long playlistId) { return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io()); } @@ -90,4 +92,28 @@ public class LocalPlaylistManager { return Single.fromCallable(() -> playlistTable.deletePlaylist(playlistId)) .subscribeOn(Schedulers.io()); } + + public Maybe renamePlaylist(final long playlistId, final String name) { + return modifyPlaylist(playlistId, name, null); + } + + public Maybe changePlaylistThumbnail(final long playlistId, + final String thumbnailUrl) { + return modifyPlaylist(playlistId, null, thumbnailUrl); + } + + private Maybe modifyPlaylist(final long playlistId, + @Nullable final String name, + @Nullable final String thumbnailUrl) { + return playlistTable.getPlaylist(playlistId) + .firstElement() + .filter(playlistEntities -> !playlistEntities.isEmpty()) + .map(playlistEntities -> { + PlaylistEntity playlist = playlistEntities.get(0); + if (name != null) playlist.setName(name); + if (thumbnailUrl != null) playlist.setThumbnailUrl(thumbnailUrl); + return playlistTable.update(playlist); + }).subscribeOn(Schedulers.io()); + } + } 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 de854ae0c..6ed357e36 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 @@ -19,8 +19,8 @@ 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.OnInfoItemGesture; import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem; import org.schabi.newpipe.playlist.PlayQueueItem; @@ -97,7 +97,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog { newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog()); - playlistAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + playlistAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture() { @Override public void selected(PlaylistInfoItem selectedItem) { if (!(selectedItem instanceof LocalPlaylistInfoItem) || getStreams() == null) @@ -113,9 +113,6 @@ public final class PlaylistAppendDialog extends PlaylistDialog { getDialog().dismiss(); } - - @Override - public void held(PlaylistInfoItem selectedItem) {} }); playlistManager.getPlaylists() 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 386ac1819..791e90fa2 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 @@ -35,8 +35,7 @@ public final class PlaylistCreationDialog extends PlaylistDialog { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { if (getStreams() == null) return super.onCreateDialog(savedInstanceState); - View dialogView = View.inflate(getContext(), - R.layout.dialog_create_playlist, null); + View dialogView = View.inflate(getContext(), R.layout.dialog_playlist_name, null); EditText nameInput = dialogView.findViewById(R.id.playlist_name); final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext()) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/local/StatisticsPlaylistFragment.java index 8db1f8780..c2181ca8d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/StatisticsPlaylistFragment.java @@ -19,8 +19,8 @@ import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.info_list.stored.StreamStatisticsInfoItem; import org.schabi.newpipe.playlist.PlayQueue; import org.schabi.newpipe.playlist.SinglePlayQueue; @@ -127,7 +127,7 @@ public abstract class StatisticsPlaylistFragment protected void initListeners() { super.initListeners(); - infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture() { @Override public void selected(StreamInfoItem selectedItem) { NavigationHelper.openVideoDetailFragment(getFragmentManager(), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index 662f617bb..8db5d5f00 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -16,8 +16,8 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; -import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.info_list.OnInfoItemGesture; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; @@ -125,24 +125,17 @@ public class SubscriptionFragment extends BaseStateFragment() { + infoListAdapter.setOnChannelSelectedListener(new OnInfoItemGesture() { @Override public void selected(ChannelInfoItem selectedItem) { // Requires the parent fragment to find holder for fragment replacement - NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.getServiceId(), selectedItem.url, selectedItem.getName()); - - } - - @Override - public void held(ChannelInfoItem selectedItem) {} - }); - - headerRootLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager()); + NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), + selectedItem.getServiceId(), selectedItem.url, selectedItem.getName()); } }); + + headerRootLayout.setOnClickListener(view -> + NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager())); } private void resetFragment() { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java index c81235623..cdad31674 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java @@ -43,17 +43,12 @@ import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder; public class InfoItemBuilder { private static final String TAG = InfoItemBuilder.class.toString(); - public interface OnInfoItemSelectedListener { - void selected(T selectedItem); - void held(T selectedItem); - } - private final Context context; private ImageLoader imageLoader = ImageLoader.getInstance(); - private OnInfoItemSelectedListener onStreamSelectedListener; - private OnInfoItemSelectedListener onChannelSelectedListener; - private OnInfoItemSelectedListener onPlaylistSelectedListener; + private OnInfoItemGesture onStreamSelectedListener; + private OnInfoItemGesture onChannelSelectedListener; + private OnInfoItemGesture onPlaylistSelectedListener; public InfoItemBuilder(Context context) { this.context = context; @@ -91,27 +86,27 @@ public class InfoItemBuilder { return imageLoader; } - public OnInfoItemSelectedListener getOnStreamSelectedListener() { + public OnInfoItemGesture getOnStreamSelectedListener() { return onStreamSelectedListener; } - public void setOnStreamSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnStreamSelectedListener(OnInfoItemGesture listener) { this.onStreamSelectedListener = listener; } - public OnInfoItemSelectedListener getOnChannelSelectedListener() { + public OnInfoItemGesture getOnChannelSelectedListener() { return onChannelSelectedListener; } - public void setOnChannelSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnChannelSelectedListener(OnInfoItemGesture listener) { this.onChannelSelectedListener = listener; } - public OnInfoItemSelectedListener getOnPlaylistSelectedListener() { + public OnInfoItemGesture getOnPlaylistSelectedListener() { return onPlaylistSelectedListener; } - public void setOnPlaylistSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnPlaylistSelectedListener(OnInfoItemGesture listener) { this.onPlaylistSelectedListener = listener; } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 5494eae23..1dc4442c7 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -10,7 +10,6 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.info_list.InfoItemBuilder.OnInfoItemSelectedListener; import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder; import org.schabi.newpipe.info_list.holder.InfoItemHolder; @@ -56,6 +55,10 @@ public class InfoListAdapter extends RecyclerView.Adapter infoItemList; private boolean useMiniVariant = false; @@ -77,15 +80,15 @@ public class InfoListAdapter extends RecyclerView.Adapter(); } - public void setOnStreamSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnStreamSelectedListener(OnInfoItemGesture listener) { infoItemBuilder.setOnStreamSelectedListener(listener); } - public void setOnChannelSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnChannelSelectedListener(OnInfoItemGesture listener) { infoItemBuilder.setOnChannelSelectedListener(listener); } - public void setOnPlaylistSelectedListener(OnInfoItemSelectedListener listener) { + public void setOnPlaylistSelectedListener(OnInfoItemGesture listener) { infoItemBuilder.setOnPlaylistSelectedListener(listener); } @@ -202,7 +205,7 @@ public class InfoListAdapter extends RecyclerView.Adapter { + + public abstract void selected(T selectedItem); + + public void held(T selectedItem) { + // Optional gesture + } + + public void drag(T selectedItem, RecyclerView.ViewHolder viewHolder) { + // Optional gesture + } +} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamPlaylistInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamPlaylistInfoItemHolder.java new file mode 100644 index 000000000..8261d4760 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamPlaylistInfoItemHolder.java @@ -0,0 +1,102 @@ +package org.schabi.newpipe.info_list.holder; + +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.MotionEvent; +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.stream.StreamInfoItem; +import org.schabi.newpipe.info_list.InfoItemBuilder; +import org.schabi.newpipe.util.Localization; + +public class StreamPlaylistInfoItemHolder extends InfoItemHolder { + + public final ImageView itemThumbnailView; + public final TextView itemVideoTitleView; + public final TextView itemUploaderView; + public final TextView itemDurationView; + public final View itemHandleView; + + StreamPlaylistInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) { + super(infoItemBuilder, layoutId, parent); + + itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView); + itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView); + itemUploaderView = itemView.findViewById(R.id.itemUploaderView); + itemDurationView = itemView.findViewById(R.id.itemDurationView); + itemHandleView = itemView.findViewById(R.id.itemHandle); + } + + public StreamPlaylistInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) { + this(infoItemBuilder, R.layout.list_playlist_mini_item, parent); + } + + @Override + public void updateFromItem(final InfoItem infoItem) { + if (!(infoItem instanceof StreamInfoItem)) return; + final StreamInfoItem item = (StreamInfoItem) infoItem; + + itemVideoTitleView.setText(item.getName()); + itemUploaderView.setText(item.uploader_name); + + if (item.duration > 0) { + itemDurationView.setText(Localization.getDurationString(item.duration)); + itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(), + R.color.duration_background_color)); + itemDurationView.setVisibility(View.VISIBLE); + } else { + itemDurationView.setVisibility(View.GONE); + } + + // Default thumbnail is shown on error, while loading and if the url is empty + itemBuilder.getImageLoader().displayImage(item.thumbnail_url, itemThumbnailView, + StreamPlaylistInfoItemHolder.DISPLAY_THUMBNAIL_OPTIONS); + + itemView.setOnClickListener(view -> { + if (itemBuilder.getOnStreamSelectedListener() != null) { + itemBuilder.getOnStreamSelectedListener().selected(item); + } + }); + + itemView.setLongClickable(true); + itemView.setOnLongClickListener(view -> { + if (itemBuilder.getOnStreamSelectedListener() != null) { + itemBuilder.getOnStreamSelectedListener().held(item); + } + return true; + }); + + itemThumbnailView.setOnTouchListener(getOnTouchListener(item)); + itemHandleView.setOnTouchListener(getOnTouchListener(item)); + } + + private View.OnTouchListener getOnTouchListener(final StreamInfoItem item) { + return (view, motionEvent) -> { + view.performClick(); + if (itemBuilder != null && + motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemBuilder.getOnStreamSelectedListener() + .drag(item, StreamPlaylistInfoItemHolder.this); + } + return false; + }; + } + + /** + * Display options for stream thumbnails + */ + private static final DisplayImageOptions DISPLAY_THUMBNAIL_OPTIONS = + new DisplayImageOptions.Builder() + .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) + .showImageOnFail(R.drawable.dummy_thumbnail) + .showImageForEmptyUri(R.drawable.dummy_thumbnail) + .showImageOnLoading(R.drawable.dummy_thumbnail) + .build(); +} diff --git a/app/src/main/res/layout/dialog_create_playlist.xml b/app/src/main/res/layout/dialog_playlist_name.xml similarity index 96% rename from app/src/main/res/layout/dialog_create_playlist.xml rename to app/src/main/res/layout/dialog_playlist_name.xml index b42d3101f..2dfab228b 100644 --- a/app/src/main/res/layout/dialog_create_playlist.xml +++ b/app/src/main/res/layout/dialog_playlist_name.xml @@ -1,6 +1,5 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 361f453c4..161cf1735 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -188,6 +188,7 @@ No results @string/no_videos Nothing Here But Crickets + Drag to reorder Cannot create download directory \'%1$s\' Created download directory \'%1$s\'