-Added ability to save playlist as remote playlist link rather than storing it in database.
-Added LeakCanary as part of debug build. -Modified bookmark list to show both remote and local playlists. -Removed ability to save channel items as local playlist, in favor of subscribe.
|
@ -89,4 +89,6 @@ dependencies {
|
|||
|
||||
implementation 'frankiesardo:icepick:3.2.0'
|
||||
annotationProcessor 'frankiesardo:icepick-processor:3.2.0'
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||
import android.support.multidex.MultiDex;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
public class DebugApp extends App {
|
||||
private static final String TAG = DebugApp.class.toString();
|
||||
|
@ -18,6 +19,13 @@ public class DebugApp extends App {
|
|||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
if (LeakCanary.isInAnalyzerProcess(this)) {
|
||||
// This process is dedicated to LeakCanary for heap analysis.
|
||||
// You should not init your app in this process.
|
||||
return;
|
||||
}
|
||||
LeakCanary.install(this);
|
||||
|
||||
initStetho();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@ import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
|||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||
import org.schabi.newpipe.database.playlist.dao.PlaylistDAO;
|
||||
import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO;
|
||||
import org.schabi.newpipe.database.playlist.dao.PlaylistStreamDAO;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
||||
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
||||
|
@ -26,7 +28,7 @@ import static org.schabi.newpipe.database.Migrations.DB_VER_12_0;
|
|||
entities = {
|
||||
SubscriptionEntity.class, SearchHistoryEntry.class,
|
||||
StreamEntity.class, StreamHistoryEntity.class, StreamStateEntity.class,
|
||||
PlaylistEntity.class, PlaylistStreamEntity.class
|
||||
PlaylistEntity.class, PlaylistStreamEntity.class, PlaylistRemoteEntity.class
|
||||
},
|
||||
version = DB_VER_12_0,
|
||||
exportSchema = false
|
||||
|
@ -48,4 +50,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
public abstract PlaylistDAO playlistDAO();
|
||||
|
||||
public abstract PlaylistStreamDAO playlistStreamDAO();
|
||||
|
||||
public abstract PlaylistRemoteDAO playlistRemoteDAO();
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@ package org.schabi.newpipe.database;
|
|||
|
||||
public interface LocalItem {
|
||||
enum LocalItemType {
|
||||
PLAYLIST_ITEM,
|
||||
PLAYLIST_LOCAL_ITEM,
|
||||
PLAYLIST_REMOTE_ITEM,
|
||||
|
||||
PLAYLIST_STREAM_ITEM,
|
||||
STATISTIC_STREAM_ITEM
|
||||
STATISTIC_STREAM_ITEM,
|
||||
}
|
||||
|
||||
LocalItemType getLocalItemType();
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package org.schabi.newpipe.database.playlist;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
|
||||
public interface PlaylistLocalItem extends LocalItem {
|
||||
String getOrderingName();
|
||||
}
|
|
@ -2,13 +2,11 @@ package org.schabi.newpipe.database.playlist;
|
|||
|
||||
import android.arch.persistence.room.ColumnInfo;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
|
||||
|
||||
public class PlaylistMetadataEntry implements LocalItem {
|
||||
public class PlaylistMetadataEntry implements PlaylistLocalItem {
|
||||
final public static String PLAYLIST_STREAM_COUNT = "streamCount";
|
||||
|
||||
@ColumnInfo(name = PLAYLIST_ID)
|
||||
|
@ -29,6 +27,11 @@ public class PlaylistMetadataEntry implements LocalItem {
|
|||
|
||||
@Override
|
||||
public LocalItemType getLocalItemType() {
|
||||
return LocalItemType.PLAYLIST_ITEM;
|
||||
return LocalItemType.PLAYLIST_LOCAL_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOrderingName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.schabi.newpipe.database.playlist.dao;
|
||||
|
||||
import android.arch.persistence.room.Dao;
|
||||
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.PlaylistRemoteEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL;
|
||||
|
||||
@Dao
|
||||
public abstract class PlaylistRemoteDAO implements BasicDAO<PlaylistRemoteEntity> {
|
||||
@Override
|
||||
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE)
|
||||
public abstract Flowable<List<PlaylistRemoteEntity>> getAll();
|
||||
|
||||
@Override
|
||||
@Query("DELETE FROM " + REMOTE_PLAYLIST_TABLE)
|
||||
public abstract int deleteAll();
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE +
|
||||
" WHERE " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")
|
||||
public abstract Flowable<List<PlaylistRemoteEntity>> listByService(int serviceId);
|
||||
|
||||
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE " +
|
||||
REMOTE_PLAYLIST_URL + " = :url AND " +
|
||||
REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")
|
||||
public abstract Flowable<List<PlaylistRemoteEntity>> getPlaylist(long serviceId, String url);
|
||||
|
||||
@Query("SELECT " + REMOTE_PLAYLIST_ID + " FROM " + REMOTE_PLAYLIST_TABLE +
|
||||
" WHERE " +
|
||||
REMOTE_PLAYLIST_URL + " = :url AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")
|
||||
abstract Long getPlaylistIdInternal(long serviceId, String url);
|
||||
|
||||
@Transaction
|
||||
public long upsert(PlaylistRemoteEntity playlist) {
|
||||
final Long playlistId = getPlaylistIdInternal(playlist.getServiceId(), playlist.getUrl());
|
||||
|
||||
if (playlistId == null) {
|
||||
return insert(playlist);
|
||||
} else {
|
||||
playlist.setUid(playlistId);
|
||||
update(playlist);
|
||||
return playlistId;
|
||||
}
|
||||
}
|
||||
|
||||
@Query("DELETE FROM " + REMOTE_PLAYLIST_TABLE +
|
||||
" WHERE " + REMOTE_PLAYLIST_ID + " = :playlistId")
|
||||
public abstract int deletePlaylist(final long playlistId);
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package org.schabi.newpipe.database.playlist.model;
|
||||
|
||||
import android.arch.persistence.room.ColumnInfo;
|
||||
import android.arch.persistence.room.Entity;
|
||||
import android.arch.persistence.room.Ignore;
|
||||
import android.arch.persistence.room.Index;
|
||||
import android.arch.persistence.room.PrimaryKey;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
|
||||
import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL;
|
||||
|
||||
@Entity(tableName = REMOTE_PLAYLIST_TABLE,
|
||||
indices = {
|
||||
@Index(value = {REMOTE_PLAYLIST_NAME}),
|
||||
@Index(value = {REMOTE_PLAYLIST_SERVICE_ID, REMOTE_PLAYLIST_URL}, unique = true)
|
||||
})
|
||||
public class PlaylistRemoteEntity implements PlaylistLocalItem {
|
||||
final public static String REMOTE_PLAYLIST_TABLE = "remote_playlists";
|
||||
final public static String REMOTE_PLAYLIST_ID = "uid";
|
||||
final public static String REMOTE_PLAYLIST_SERVICE_ID = "service_id";
|
||||
final public static String REMOTE_PLAYLIST_NAME = "name";
|
||||
final public static String REMOTE_PLAYLIST_URL = "url";
|
||||
final public static String REMOTE_PLAYLIST_THUMBNAIL_URL = "thumbnail_url";
|
||||
final public static String REMOTE_PLAYLIST_UPLOADER_NAME = "uploader";
|
||||
final public static String REMOTE_PLAYLIST_STREAM_COUNT = "stream_count";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_ID)
|
||||
private long uid = 0;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_SERVICE_ID)
|
||||
private int serviceId = Constants.NO_SERVICE_ID;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_NAME)
|
||||
private String name;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_URL)
|
||||
private String url;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_THUMBNAIL_URL)
|
||||
private String thumbnailUrl;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_UPLOADER_NAME)
|
||||
private String uploader;
|
||||
|
||||
@ColumnInfo(name = REMOTE_PLAYLIST_STREAM_COUNT)
|
||||
private Long streamCount;
|
||||
|
||||
public PlaylistRemoteEntity(int serviceId, String name, String url, String thumbnailUrl,
|
||||
String uploader, Long streamCount) {
|
||||
this.serviceId = serviceId;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.uploader = uploader;
|
||||
this.streamCount = streamCount;
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public PlaylistRemoteEntity(final PlaylistInfo info) {
|
||||
this(info.getServiceId(), info.getName(), info.getUrl(), info.getThumbnailUrl(),
|
||||
info.getUploaderName(), info.getStreamCount());
|
||||
}
|
||||
|
||||
public long getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(long uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public int getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public void setServiceId(int serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getThumbnailUrl() {
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
public void setThumbnailUrl(String thumbnailUrl) {
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUploader() {
|
||||
return uploader;
|
||||
}
|
||||
|
||||
public void setUploader(String uploader) {
|
||||
this.uploader = uploader;
|
||||
}
|
||||
|
||||
public Long getStreamCount() {
|
||||
return streamCount;
|
||||
}
|
||||
|
||||
public void setStreamCount(Long streamCount) {
|
||||
this.streamCount = streamCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalItemType getLocalItemType() {
|
||||
return PLAYLIST_REMOTE_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOrderingName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -290,20 +290,4 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
public void handleNextItems(N result) {
|
||||
isLoading.set(false);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void appendToPlaylist(final android.support.v4.app.FragmentManager manager,
|
||||
final String tag) {
|
||||
if (infoListAdapter == null) return;
|
||||
List<StreamInfoItem> streams = new ArrayList<>();
|
||||
for (final InfoItem item : infoListAdapter.getItemsList()) {
|
||||
if (item instanceof StreamInfoItem) {
|
||||
streams.add((StreamInfoItem) item);
|
||||
}
|
||||
}
|
||||
PlaylistAppendDialog.fromStreamInfoItems(streams).show(manager, tag);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
private LinearLayout headerBackgroundButton;
|
||||
|
||||
private MenuItem menuRssButton;
|
||||
private MenuItem playlistAppendButton;
|
||||
|
||||
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
||||
ChannelFragment instance = new ChannelFragment();
|
||||
|
@ -203,12 +202,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu +
|
||||
"], inflater = [" + inflater + "]");
|
||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||
playlistAppendButton = menu.findItem(R.id.menu_append_playlist);
|
||||
|
||||
if (currentInfo != null) {
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl()));
|
||||
playlistAppendButton.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,9 +225,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
case R.id.menu_item_share:
|
||||
shareUrl(name, url);
|
||||
break;
|
||||
case R.id.menu_append_playlist:
|
||||
appendToPlaylist(getFragmentManager(), TAG);
|
||||
break;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -434,8 +424,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
if (playlistAppendButton != null) playlistAppendButton
|
||||
.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
|
|
|
@ -17,13 +17,18 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||
|
@ -32,12 +37,21 @@ import org.schabi.newpipe.report.UserAction;
|
|||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
private CompositeDisposable disposables;
|
||||
private Subscription bookmarkReactor;
|
||||
|
||||
private RemotePlaylistManager remotePlaylistManager;
|
||||
private PlaylistRemoteEntity playlistEntity;
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -54,7 +68,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
private View headerPopupButton;
|
||||
private View headerBackgroundButton;
|
||||
|
||||
private MenuItem playlistAppendButton;
|
||||
private MenuItem playlistBookmarkButton;
|
||||
private MenuItem playlistUnbookmarkButton;
|
||||
|
||||
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
||||
PlaylistFragment instance = new PlaylistFragment();
|
||||
|
@ -67,7 +82,15 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
disposables = new CompositeDisposable();
|
||||
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_playlist, container, false);
|
||||
}
|
||||
|
||||
|
@ -96,6 +119,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
infoListAdapter.useMiniItemVariants(true);
|
||||
|
||||
remotePlaylistManager.getPlaylist(serviceId, url)
|
||||
.onBackpressureLatest()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistBookmarkSubscriber());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,29 +140,26 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
context.getResources().getString(R.string.start_here_on_popup),
|
||||
};
|
||||
|
||||
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
||||
switch (i) {
|
||||
case 0:
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 2:
|
||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 3:
|
||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 4:
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
||||
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
||||
switch (i) {
|
||||
case 0:
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 1:
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||
break;
|
||||
case 2:
|
||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 3:
|
||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||
break;
|
||||
case 4:
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -148,10 +173,28 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_playlist, menu);
|
||||
|
||||
playlistAppendButton = menu.findItem(R.id.menu_append_playlist);
|
||||
if (currentInfo != null) {
|
||||
playlistAppendButton.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
||||
}
|
||||
playlistBookmarkButton = menu.findItem(R.id.menu_item_bookmark);
|
||||
playlistUnbookmarkButton = menu.findItem(R.id.menu_item_unbookmark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (bookmarkReactor != null) bookmarkReactor.cancel();
|
||||
|
||||
bookmarkReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (disposables != null) disposables.dispose();
|
||||
|
||||
disposables = null;
|
||||
remotePlaylistManager = null;
|
||||
playlistEntity = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -177,8 +220,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
case R.id.menu_item_share:
|
||||
shareUrl(name, url);
|
||||
break;
|
||||
case R.id.menu_append_playlist:
|
||||
appendToPlaylist(getFragmentManager(), TAG);
|
||||
case R.id.menu_item_bookmark:
|
||||
bookmarkPlaylist();
|
||||
break;
|
||||
case R.id.menu_item_unbookmark:
|
||||
unbookmarkPlaylist();
|
||||
break;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
@ -211,12 +257,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
if (!TextUtils.isEmpty(result.getUploaderName())) {
|
||||
headerUploaderName.setText(result.getUploaderName());
|
||||
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
|
||||
headerUploaderLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
NavigationHelper.openChannelFragment(getFragmentManager(), result.getServiceId(), result.getUploaderUrl(), result.getUploaderName());
|
||||
}
|
||||
});
|
||||
headerUploaderLayout.setOnClickListener(v ->
|
||||
NavigationHelper.openChannelFragment(getFragmentManager(),
|
||||
result.getServiceId(), result.getUploaderUrl(),
|
||||
result.getUploaderName())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,31 +270,20 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
||||
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
|
||||
|
||||
if (playlistAppendButton != null) playlistAppendButton
|
||||
.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
remotePlaylistManager.onUpdate(result)
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(integer -> {/* Do nothing*/}, this::onError);
|
||||
|
||||
headerPlayAllButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
headerPopupButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
|
@ -293,9 +327,64 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private Subscriber<List<PlaylistRemoteEntity>> getPlaylistBookmarkSubscriber() {
|
||||
return new Subscriber<List<PlaylistRemoteEntity>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
if (bookmarkReactor != null) bookmarkReactor.cancel();
|
||||
bookmarkReactor = s;
|
||||
bookmarkReactor.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<PlaylistRemoteEntity> playlist) {
|
||||
if (playlistBookmarkButton == null || playlistUnbookmarkButton == null) return;
|
||||
|
||||
playlistBookmarkButton.setVisible(playlist.isEmpty());
|
||||
playlistUnbookmarkButton.setVisible(!playlist.isEmpty());
|
||||
playlistEntity = playlist.isEmpty() ? null : playlist.get(0);
|
||||
|
||||
if (bookmarkReactor != null) bookmarkReactor.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
PlaylistFragment.this.onError(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
super.setTitle(title);
|
||||
headerTitleView.setText(title);
|
||||
}
|
||||
|
||||
private void bookmarkPlaylist() {
|
||||
if (remotePlaylistManager == null || currentInfo == null) return;
|
||||
|
||||
playlistBookmarkButton.setVisible(false);
|
||||
playlistUnbookmarkButton.setVisible(false);
|
||||
|
||||
remotePlaylistManager.onBookmark(currentInfo)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {/* Do nothing */}, this::onError);
|
||||
}
|
||||
|
||||
private void unbookmarkPlaylist() {
|
||||
if (remotePlaylistManager == null || playlistEntity == null) return;
|
||||
|
||||
playlistBookmarkButton.setVisible(false);
|
||||
playlistUnbookmarkButton.setVisible(false);
|
||||
|
||||
remotePlaylistManager.deletePlaylist(playlistEntity.getUid())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doFinally(() -> playlistEntity = null)
|
||||
.subscribe(ignored -> {/* Do nothing */}, this::onError);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ import org.schabi.newpipe.fragments.local.holder.LocalItemHolder;
|
|||
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.LocalStatisticStreamItemHolder;
|
||||
import org.schabi.newpipe.fragments.local.holder.RemotePlaylistItemHolder;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
|
@ -49,8 +49,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
|
||||
private static final int STREAM_STATISTICS_HOLDER_TYPE = 0x1000;
|
||||
private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001;
|
||||
private static final int PLAYLIST_HOLDER_TYPE = 0x2000;
|
||||
|
||||
private static final int LOCAL_PLAYLIST_HOLDER_TYPE = 0x2000;
|
||||
private static final int REMOTE_PLAYLIST_HOLDER_TYPE = 0x2001;
|
||||
|
||||
private final LocalItemBuilder localItemBuilder;
|
||||
private final ArrayList<LocalItem> localItems;
|
||||
private final DateFormat dateFormat;
|
||||
|
@ -187,7 +188,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
final LocalItem item = localItems.get(position);
|
||||
|
||||
switch (item.getLocalItemType()) {
|
||||
case PLAYLIST_ITEM: return PLAYLIST_HOLDER_TYPE;
|
||||
case PLAYLIST_LOCAL_ITEM: return LOCAL_PLAYLIST_HOLDER_TYPE;
|
||||
case PLAYLIST_REMOTE_ITEM: return REMOTE_PLAYLIST_HOLDER_TYPE;
|
||||
|
||||
case PLAYLIST_STREAM_ITEM: return STREAM_PLAYLIST_HOLDER_TYPE;
|
||||
case STATISTIC_STREAM_ITEM: return STREAM_STATISTICS_HOLDER_TYPE;
|
||||
default:
|
||||
|
@ -206,8 +209,10 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
return new HeaderFooterHolder(header);
|
||||
case FOOTER_TYPE:
|
||||
return new HeaderFooterHolder(footer);
|
||||
case PLAYLIST_HOLDER_TYPE:
|
||||
case LOCAL_PLAYLIST_HOLDER_TYPE:
|
||||
return new LocalPlaylistItemHolder(localItemBuilder, parent);
|
||||
case REMOTE_PLAYLIST_HOLDER_TYPE:
|
||||
return new RemotePlaylistItemHolder(localItemBuilder, parent);
|
||||
case STREAM_PLAYLIST_HOLDER_TYPE:
|
||||
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
|
||||
case STREAM_STATISTICS_HOLDER_TYPE:
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package org.schabi.newpipe.fragments.local;
|
||||
|
||||
import org.schabi.newpipe.database.AppDatabase;
|
||||
import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class RemotePlaylistManager {
|
||||
|
||||
private final AppDatabase database;
|
||||
private final PlaylistRemoteDAO playlistRemoteTable;
|
||||
|
||||
public RemotePlaylistManager(final AppDatabase db) {
|
||||
database = db;
|
||||
playlistRemoteTable = db.playlistRemoteDAO();
|
||||
}
|
||||
|
||||
public Flowable<List<PlaylistRemoteEntity>> getPlaylists() {
|
||||
return playlistRemoteTable.getAll().subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Flowable<List<PlaylistRemoteEntity>> getPlaylist(final int serviceId, final String url) {
|
||||
return playlistRemoteTable.getPlaylist(serviceId, url).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Integer> deletePlaylist(final long playlistId) {
|
||||
return Single.fromCallable(() -> playlistRemoteTable.deletePlaylist(playlistId))
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Long> onBookmark(final PlaylistInfo playlistInfo) {
|
||||
return Single.fromCallable(() -> {
|
||||
final PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo);
|
||||
return playlistRemoteTable.upsert(playlist);
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<Integer> onUpdate(final PlaylistInfo playlistInfo) {
|
||||
return Single.fromCallable(() -> playlistRemoteTable.update(new PlaylistRemoteEntity(playlistInfo)))
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import android.os.Bundle;
|
|||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -14,23 +14,30 @@ import org.reactivestreams.Subscriber;
|
|||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.AppDatabase;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.fragments.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
public final class BookmarkFragment
|
||||
extends BaseLocalListFragment<List<PlaylistMetadataEntry>, Void> {
|
||||
extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
|
||||
|
||||
private View watchHistoryButton;
|
||||
private View mostWatchedButton;
|
||||
|
@ -41,6 +48,7 @@ public final class BookmarkFragment
|
|||
private Subscription databaseSubscription;
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
private LocalPlaylistManager localPlaylistManager;
|
||||
private RemotePlaylistManager remotePlaylistManager;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle - Creation
|
||||
|
@ -49,7 +57,9 @@ public final class BookmarkFragment
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
localPlaylistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
final AppDatabase database = NewPipeDatabase.getInstance(getContext());
|
||||
localPlaylistManager = new LocalPlaylistManager(database);
|
||||
remotePlaylistManager = new RemotePlaylistManager(database);
|
||||
disposables = new CompositeDisposable();
|
||||
}
|
||||
|
||||
|
@ -99,17 +109,28 @@ public final class BookmarkFragment
|
|||
@Override
|
||||
public void selected(LocalItem selectedItem) {
|
||||
// Requires the parent fragment to find holder for fragment replacement
|
||||
if (selectedItem instanceof PlaylistMetadataEntry && getParentFragment() != null) {
|
||||
if (getParentFragment() == null) return;
|
||||
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
|
||||
|
||||
if (selectedItem instanceof PlaylistMetadataEntry) {
|
||||
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
|
||||
NavigationHelper.openLocalPlaylistFragment(
|
||||
getParentFragment().getFragmentManager(), entry.uid, entry.name);
|
||||
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
|
||||
entry.name);
|
||||
|
||||
} else if (selectedItem instanceof PlaylistRemoteEntity) {
|
||||
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
|
||||
NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(),
|
||||
entry.getUrl(), entry.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(LocalItem selectedItem) {
|
||||
if (selectedItem instanceof PlaylistMetadataEntry) {
|
||||
showDeleteDialog((PlaylistMetadataEntry) selectedItem);
|
||||
showLocalDeleteDialog((PlaylistMetadataEntry) selectedItem);
|
||||
|
||||
} else if (selectedItem instanceof PlaylistRemoteEntity) {
|
||||
showRemoteDeleteDialog((PlaylistRemoteEntity) selectedItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -134,9 +155,14 @@ public final class BookmarkFragment
|
|||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
localPlaylistManager.getPlaylists()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getSubscriptionSubscriber());
|
||||
|
||||
Flowable.combineLatest(
|
||||
localPlaylistManager.getPlaylists(),
|
||||
remotePlaylistManager.getPlaylists(),
|
||||
BookmarkFragment::merge
|
||||
).onBackpressureLatest()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistsSubscriber());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -165,6 +191,7 @@ public final class BookmarkFragment
|
|||
|
||||
disposables = null;
|
||||
localPlaylistManager = null;
|
||||
remotePlaylistManager = null;
|
||||
itemsListState = null;
|
||||
}
|
||||
|
||||
|
@ -172,8 +199,8 @@ public final class BookmarkFragment
|
|||
// Subscriptions Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Subscriber<List<PlaylistMetadataEntry>> getSubscriptionSubscriber() {
|
||||
return new Subscriber<List<PlaylistMetadataEntry>>() {
|
||||
private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() {
|
||||
return new Subscriber<List<PlaylistLocalItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
showLoading();
|
||||
|
@ -183,7 +210,7 @@ public final class BookmarkFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<PlaylistMetadataEntry> subscriptions) {
|
||||
public void onNext(List<PlaylistLocalItem> subscriptions) {
|
||||
handleResult(subscriptions);
|
||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||
}
|
||||
|
@ -200,7 +227,7 @@ public final class BookmarkFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull List<PlaylistMetadataEntry> result) {
|
||||
public void handleResult(@NonNull List<PlaylistLocalItem> result) {
|
||||
super.handleResult(result);
|
||||
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
@ -240,25 +267,41 @@ public final class BookmarkFragment
|
|||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void showDeleteDialog(final PlaylistMetadataEntry item) {
|
||||
private void showLocalDeleteDialog(final PlaylistMetadataEntry item) {
|
||||
showDeleteDialog(item.name, localPlaylistManager.deletePlaylist(item.uid));
|
||||
}
|
||||
|
||||
private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) {
|
||||
showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid()));
|
||||
}
|
||||
|
||||
private void showDeleteDialog(final String name, final Single<Integer> deleteReactor) {
|
||||
if (activity == null || disposables == null) return;
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(item.name)
|
||||
.setTitle(name)
|
||||
.setMessage(R.string.delete_playlist_prompt)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.delete, (dialog, i) ->
|
||||
disposables.add(deletePlaylist(item.uid))
|
||||
disposables.add(deleteReactor
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {/*Do nothing on success*/}, this::onError))
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private Disposable deletePlaylist(final long playlistId) {
|
||||
return localPlaylistManager.deletePlaylist(playlistId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {/*Do nothing on success*/},
|
||||
throwable -> Log.e(TAG, "Playlist deletion failed, id=["
|
||||
+ playlistId + "]")
|
||||
);
|
||||
private static List<PlaylistLocalItem> merge(final List<PlaylistMetadataEntry> localPlaylists,
|
||||
final List<PlaylistRemoteEntity> remotePlaylists) {
|
||||
List<PlaylistLocalItem> items = new ArrayList<>(
|
||||
localPlaylists.size() + remotePlaylists.size());
|
||||
items.addAll(localPlaylists);
|
||||
items.addAll(remotePlaylists);
|
||||
|
||||
Collections.sort(items, (left, right) ->
|
||||
left.getOrderingName().compareToIgnoreCase(right.getOrderingName()));
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,24 +14,10 @@ import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
|||
|
||||
import java.text.DateFormat;
|
||||
|
||||
public class LocalPlaylistItemHolder extends LocalItemHolder {
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemStreamCountView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
|
||||
public LocalPlaylistItemHolder(LocalItemBuilder 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 class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
||||
|
||||
public LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
this(infoItemBuilder, R.layout.list_playlist_mini_item, parent);
|
||||
super(infoItemBuilder, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,29 +31,6 @@ public class LocalPlaylistItemHolder extends LocalItemHolder {
|
|||
|
||||
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, DISPLAY_THUMBNAIL_OPTIONS);
|
||||
|
||||
itemView.setOnClickListener(view -> {
|
||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||
itemBuilder.getOnItemSelectedListener().selected(item);
|
||||
}
|
||||
});
|
||||
|
||||
itemView.setLongClickable(true);
|
||||
itemView.setOnLongClickListener(view -> {
|
||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||
itemBuilder.getOnItemSelectedListener().held(item);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
super.updateFromItem(localItem, dateFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.fragments.local.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.database.LocalItem;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
||||
public abstract class PlaylistItemHolder extends LocalItemHolder {
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemStreamCountView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
|
||||
public PlaylistItemHolder(LocalItemBuilder 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 PlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
this(infoItemBuilder, R.layout.list_playlist_mini_item, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
itemView.setOnClickListener(view -> {
|
||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||
itemBuilder.getOnItemSelectedListener().selected(localItem);
|
||||
}
|
||||
});
|
||||
|
||||
itemView.setLongClickable(true);
|
||||
itemView.setOnLongClickListener(view -> {
|
||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||
itemBuilder.getOnItemSelectedListener().held(localItem);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,33 @@
|
|||
package org.schabi.newpipe.fragments.local.holder;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
||||
public class RemotePlaylistItemHolder extends PlaylistItemHolder {
|
||||
public RemotePlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
super(infoItemBuilder, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
if (!(localItem instanceof PlaylistRemoteEntity)) return;
|
||||
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
|
||||
|
||||
itemTitleView.setText(item.getName());
|
||||
itemStreamCountView.setText(String.valueOf(item.getStreamCount()));
|
||||
itemUploaderView.setText(Localization.concatenateStrings(item.getUploader(),
|
||||
NewPipe.getNameOfService(item.getServiceId())));
|
||||
|
||||
itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView,
|
||||
DISPLAY_THUMBNAIL_OPTIONS);
|
||||
|
||||
super.updateFromItem(localItem, dateFormat);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 163 B |
After Width: | Height: | Size: 159 B |
After Width: | Height: | Size: 122 B |
After Width: | Height: | Size: 124 B |
After Width: | Height: | Size: 163 B |
After Width: | Height: | Size: 163 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 283 B |
After Width: | Height: | Size: 289 B |
|
@ -314,7 +314,7 @@
|
|||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/append_playlist"
|
||||
android:drawableTop="?attr/playlist_add"
|
||||
android:drawableTop="?attr/ic_playlist_add"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingTop="6dp"
|
||||
|
|
|
@ -22,12 +22,4 @@
|
|||
android:icon="?attr/share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_append_playlist"
|
||||
android:icon="?attr/playlist_add"
|
||||
android:title="@string/append_playlist"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom"
|
||||
tools:visible="true"/>
|
||||
</menu>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<item
|
||||
android:id="@+id/action_append_playlist"
|
||||
android:icon="?attr/playlist_add"
|
||||
android:icon="?attr/ic_playlist_add"
|
||||
android:title="@string/append_playlist"
|
||||
android:visible="true"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
|
|
@ -15,10 +15,18 @@
|
|||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_append_playlist"
|
||||
android:icon="?attr/playlist_add"
|
||||
android:title="@string/append_playlist"
|
||||
android:id="@+id/menu_item_bookmark"
|
||||
android:icon="?attr/ic_playlist_add"
|
||||
android:title="@string/bookmark_playlist"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_item_unbookmark"
|
||||
android:icon="?attr/ic_playlist_check"
|
||||
android:title="@string/unbookmark_playlist"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true"/>
|
||||
</menu>
|
|
@ -27,7 +27,8 @@
|
|||
<attr name="ic_hot" format="reference"/>
|
||||
<attr name="ic_channel" format="reference"/>
|
||||
<attr name="ic_bookmark" format="reference"/>
|
||||
<attr name="playlist_add" format="reference"/>
|
||||
<attr name="ic_playlist_add" format="reference"/>
|
||||
<attr name="ic_playlist_check" format="reference"/>
|
||||
|
||||
<!-- Can't refer to colors directly into drawable's xml-->
|
||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||
|
|
|
@ -387,6 +387,9 @@
|
|||
<string name="append_playlist">Add To Playlist</string>
|
||||
<string name="set_as_playlist_thumbnail">Set as Playlist Thumbnail</string>
|
||||
|
||||
<string name="bookmark_playlist">Bookmark Playlist</string>
|
||||
<string name="unbookmark_playlist">Remove Bookmark</string>
|
||||
|
||||
<string name="delete_playlist_prompt">Do you want to delete this playlist?</string>
|
||||
<string name="playlist_creation_success">Playlist successfully created</string>
|
||||
<string name="playlist_add_stream_success">Added to playlist</string>
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
<item name="ic_hot">@drawable/ic_whatshot_black_24dp</item>
|
||||
<item name="ic_channel">@drawable/ic_channel_black_24dp</item>
|
||||
<item name="ic_bookmark">@drawable/ic_bookmark_black_24dp</item>
|
||||
<item name="playlist_add">@drawable/ic_playlist_add_black_24dp</item>
|
||||
<item name="ic_playlist_add">@drawable/ic_playlist_add_black_24dp</item>
|
||||
<item name="ic_playlist_check">@drawable/ic_playlist_add_check_black_24dp</item>
|
||||
|
||||
<item name="separator_color">@color/light_separator_color</item>
|
||||
<item name="contrast_background_color">@color/light_contrast_background_color</item>
|
||||
|
@ -91,7 +92,8 @@
|
|||
<item name="ic_hot">@drawable/ic_whatshot_white_24dp</item>
|
||||
<item name="ic_channel">@drawable/ic_channel_white_24dp</item>
|
||||
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
|
||||
<item name="playlist_add">@drawable/ic_playlist_add_white_24dp</item>
|
||||
<item name="ic_playlist_add">@drawable/ic_playlist_add_white_24dp</item>
|
||||
<item name="ic_playlist_check">@drawable/ic_playlist_add_check_white_24dp</item>
|
||||
|
||||
<item name="separator_color">@color/dark_separator_color</item>
|
||||
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||
|
|