-Added support for changing local playlist name and thumbnail url.

-Added query to remove stream table orphans.
-Added query for retrieving flattened watch history records.
-Added holder for local playlist stream info items.
-Refactored info item on select listener as on touch gesture.
This commit is contained in:
John Zhen Mo 2018-01-25 22:24:59 -08:00
parent d98183a471
commit c3208aa415
19 changed files with 353 additions and 68 deletions

View file

@ -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<StreamInfo> implement
@Override
protected void initListeners() {
super.initListeners();
infoItemBuilder.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<StreamInfoItem>() {
infoItemBuilder.setOnStreamSelectedListener(new OnInfoItemGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());

View file

@ -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<I, N> extends BaseStateFragment<I> implem
@Override
protected void initListeners() {
super.initListeners();
infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<StreamInfoItem>() {
infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
@ -155,7 +150,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
}
});
infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<ChannelInfoItem>() {
infoListAdapter.setOnChannelSelectedListener(new OnInfoItemGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
onItemSelected(selectedItem);
@ -163,12 +158,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
@Override
public void held(ChannelInfoItem selectedItem) {}
});
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
infoListAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture<PlaylistInfoItem>() {
@Override
public void selected(PlaylistInfoItem selectedItem) {
onItemSelected(selectedItem);
@ -176,9 +168,6 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
@Override
public void held(PlaylistInfoItem selectedItem) {}
});
itemsList.clearOnScrollListeners();

View file

@ -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<List<PlaylistMetadataEnt
protected void initListeners() {
super.initListeners();
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
infoListAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture<PlaylistInfoItem>() {
@Override
public void selected(PlaylistInfoItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement

View file

@ -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<List<StreamEntity>,
protected void initListeners() {
super.initListeners();
infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<StreamInfoItem>() {
infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture<StreamInfoItem>() {
@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<List<StreamEntity>,
super.startLoading(forceLoad);
resetFragment();
playlistManager.getPlaylist(playlistId)
playlistManager.getPlaylistStreams(playlistId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistObserver());
}
@ -317,7 +317,7 @@ public class LocalPlaylistFragment extends BaseListFragment<List<StreamEntity>,
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;
}

View file

@ -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<List<StreamEntity>> getPlaylist(final long playlistId) {
public Flowable<List<StreamEntity>> 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<Integer> renamePlaylist(final long playlistId, final String name) {
return modifyPlaylist(playlistId, name, null);
}
public Maybe<Integer> changePlaylistThumbnail(final long playlistId,
final String thumbnailUrl) {
return modifyPlaylist(playlistId, null, thumbnailUrl);
}
private Maybe<Integer> 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());
}
}

View file

@ -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<PlaylistInfoItem>() {
playlistAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture<PlaylistInfoItem>() {
@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()

View file

@ -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())

View file

@ -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<StreamInfoItem>() {
infoListAdapter.setOnStreamSelectedListener(new OnInfoItemGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
NavigationHelper.openVideoDetailFragment(getFragmentManager(),

View file

@ -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<List<SubscriptionEnt
protected void initListeners() {
super.initListeners();
infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<ChannelInfoItem>() {
infoListAdapter.setOnChannelSelectedListener(new OnInfoItemGesture<ChannelInfoItem>() {
@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() {