-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'
|
implementation 'frankiesardo:icepick:3.2.0'
|
||||||
annotationProcessor 'frankiesardo:icepick-processor: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 android.support.multidex.MultiDex;
|
||||||
|
|
||||||
import com.facebook.stetho.Stetho;
|
import com.facebook.stetho.Stetho;
|
||||||
|
import com.squareup.leakcanary.LeakCanary;
|
||||||
|
|
||||||
public class DebugApp extends App {
|
public class DebugApp extends App {
|
||||||
private static final String TAG = DebugApp.class.toString();
|
private static final String TAG = DebugApp.class.toString();
|
||||||
|
@ -18,6 +19,13 @@ public class DebugApp extends App {
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.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();
|
initStetho();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
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.SearchHistoryEntry;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||||
import org.schabi.newpipe.database.playlist.dao.PlaylistDAO;
|
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.dao.PlaylistStreamDAO;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
|
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.playlist.model.PlaylistStreamEntity;
|
||||||
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
||||||
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
||||||
|
@ -26,7 +28,7 @@ import static org.schabi.newpipe.database.Migrations.DB_VER_12_0;
|
||||||
entities = {
|
entities = {
|
||||||
SubscriptionEntity.class, SearchHistoryEntry.class,
|
SubscriptionEntity.class, SearchHistoryEntry.class,
|
||||||
StreamEntity.class, StreamHistoryEntity.class, StreamStateEntity.class,
|
StreamEntity.class, StreamHistoryEntity.class, StreamStateEntity.class,
|
||||||
PlaylistEntity.class, PlaylistStreamEntity.class
|
PlaylistEntity.class, PlaylistStreamEntity.class, PlaylistRemoteEntity.class
|
||||||
},
|
},
|
||||||
version = DB_VER_12_0,
|
version = DB_VER_12_0,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
|
@ -48,4 +50,6 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
public abstract PlaylistDAO playlistDAO();
|
public abstract PlaylistDAO playlistDAO();
|
||||||
|
|
||||||
public abstract PlaylistStreamDAO playlistStreamDAO();
|
public abstract PlaylistStreamDAO playlistStreamDAO();
|
||||||
|
|
||||||
|
public abstract PlaylistRemoteDAO playlistRemoteDAO();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@ package org.schabi.newpipe.database;
|
||||||
|
|
||||||
public interface LocalItem {
|
public interface LocalItem {
|
||||||
enum LocalItemType {
|
enum LocalItemType {
|
||||||
PLAYLIST_ITEM,
|
PLAYLIST_LOCAL_ITEM,
|
||||||
|
PLAYLIST_REMOTE_ITEM,
|
||||||
|
|
||||||
PLAYLIST_STREAM_ITEM,
|
PLAYLIST_STREAM_ITEM,
|
||||||
STATISTIC_STREAM_ITEM
|
STATISTIC_STREAM_ITEM,
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalItemType getLocalItemType();
|
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 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_ID;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
|
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";
|
final public static String PLAYLIST_STREAM_COUNT = "streamCount";
|
||||||
|
|
||||||
@ColumnInfo(name = PLAYLIST_ID)
|
@ColumnInfo(name = PLAYLIST_ID)
|
||||||
|
@ -29,6 +27,11 @@ public class PlaylistMetadataEntry implements LocalItem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalItemType getLocalItemType() {
|
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) {
|
public void handleNextItems(N result) {
|
||||||
isLoading.set(false);
|
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 LinearLayout headerBackgroundButton;
|
||||||
|
|
||||||
private MenuItem menuRssButton;
|
private MenuItem menuRssButton;
|
||||||
private MenuItem playlistAppendButton;
|
|
||||||
|
|
||||||
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
||||||
ChannelFragment instance = new ChannelFragment();
|
ChannelFragment instance = new ChannelFragment();
|
||||||
|
@ -203,12 +202,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu +
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu +
|
||||||
"], inflater = [" + inflater + "]");
|
"], inflater = [" + inflater + "]");
|
||||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
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:
|
case R.id.menu_item_share:
|
||||||
shareUrl(name, url);
|
shareUrl(name, url);
|
||||||
break;
|
break;
|
||||||
case R.id.menu_append_playlist:
|
|
||||||
appendToPlaylist(getFragmentManager(), TAG);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -434,8 +424,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
} else headerSubscribersTextView.setVisibility(View.GONE);
|
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||||
|
|
||||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||||
if (playlistAppendButton != null) playlistAppendButton
|
|
||||||
.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
|
||||||
|
|
||||||
playlistCtrl.setVisibility(View.VISIBLE);
|
playlistCtrl.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,18 @@ import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
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.R;
|
||||||
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
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.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
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.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
|
||||||
public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
|
|
||||||
|
private CompositeDisposable disposables;
|
||||||
|
private Subscription bookmarkReactor;
|
||||||
|
|
||||||
|
private RemotePlaylistManager remotePlaylistManager;
|
||||||
|
private PlaylistRemoteEntity playlistEntity;
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Views
|
// Views
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -54,7 +68,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
private View headerPopupButton;
|
private View headerPopupButton;
|
||||||
private View headerBackgroundButton;
|
private View headerBackgroundButton;
|
||||||
|
|
||||||
private MenuItem playlistAppendButton;
|
private MenuItem playlistBookmarkButton;
|
||||||
|
private MenuItem playlistUnbookmarkButton;
|
||||||
|
|
||||||
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
||||||
PlaylistFragment instance = new PlaylistFragment();
|
PlaylistFragment instance = new PlaylistFragment();
|
||||||
|
@ -67,7 +82,15 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@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);
|
return inflater.inflate(R.layout.fragment_playlist, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +119,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
super.initViews(rootView, savedInstanceState);
|
super.initViews(rootView, savedInstanceState);
|
||||||
|
|
||||||
infoListAdapter.useMiniItemVariants(true);
|
infoListAdapter.useMiniItemVariants(true);
|
||||||
|
|
||||||
|
remotePlaylistManager.getPlaylist(serviceId, url)
|
||||||
|
.onBackpressureLatest()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getPlaylistBookmarkSubscriber());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -112,29 +140,26 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
context.getResources().getString(R.string.start_here_on_popup),
|
context.getResources().getString(R.string.start_here_on_popup),
|
||||||
};
|
};
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
||||||
@Override
|
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
switch (i) {
|
||||||
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
case 0:
|
||||||
switch (i) {
|
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||||
case 0:
|
break;
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
case 1:
|
||||||
break;
|
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
||||||
case 1:
|
break;
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
case 2:
|
||||||
break;
|
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||||
case 2:
|
break;
|
||||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
case 3:
|
||||||
break;
|
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||||
case 3:
|
break;
|
||||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
case 4:
|
||||||
break;
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
||||||
case 4:
|
break;
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,10 +173,28 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
inflater.inflate(R.menu.menu_playlist, menu);
|
inflater.inflate(R.menu.menu_playlist, menu);
|
||||||
|
|
||||||
playlistAppendButton = menu.findItem(R.id.menu_append_playlist);
|
playlistBookmarkButton = menu.findItem(R.id.menu_item_bookmark);
|
||||||
if (currentInfo != null) {
|
playlistUnbookmarkButton = menu.findItem(R.id.menu_item_unbookmark);
|
||||||
playlistAppendButton.setVisible(!currentInfo.getRelatedStreams().isEmpty());
|
}
|
||||||
}
|
|
||||||
|
@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:
|
case R.id.menu_item_share:
|
||||||
shareUrl(name, url);
|
shareUrl(name, url);
|
||||||
break;
|
break;
|
||||||
case R.id.menu_append_playlist:
|
case R.id.menu_item_bookmark:
|
||||||
appendToPlaylist(getFragmentManager(), TAG);
|
bookmarkPlaylist();
|
||||||
|
break;
|
||||||
|
case R.id.menu_item_unbookmark:
|
||||||
|
unbookmarkPlaylist();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -211,12 +257,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
if (!TextUtils.isEmpty(result.getUploaderName())) {
|
if (!TextUtils.isEmpty(result.getUploaderName())) {
|
||||||
headerUploaderName.setText(result.getUploaderName());
|
headerUploaderName.setText(result.getUploaderName());
|
||||||
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
|
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
|
||||||
headerUploaderLayout.setOnClickListener(new View.OnClickListener() {
|
headerUploaderLayout.setOnClickListener(v ->
|
||||||
@Override
|
NavigationHelper.openChannelFragment(getFragmentManager(),
|
||||||
public void onClick(View v) {
|
result.getServiceId(), result.getUploaderUrl(),
|
||||||
NavigationHelper.openChannelFragment(getFragmentManager(), result.getServiceId(), result.getUploaderUrl(), result.getUploaderName());
|
result.getUploaderName())
|
||||||
}
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,31 +270,20 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
|
||||||
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
|
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()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
remotePlaylistManager.onUpdate(result)
|
||||||
@Override
|
.subscribeOn(AndroidSchedulers.mainThread())
|
||||||
public void onClick(View view) {
|
.subscribe(integer -> {/* Do nothing*/}, this::onError);
|
||||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
|
|
||||||
}
|
headerPlayAllButton.setOnClickListener(view ->
|
||||||
});
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
headerPopupButton.setOnClickListener(view ->
|
||||||
@Override
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||||
public void onClick(View view) {
|
headerBackgroundButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||||
}
|
|
||||||
});
|
|
||||||
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlayQueue getPlayQueue() {
|
private PlayQueue getPlayQueue() {
|
||||||
|
@ -293,9 +327,64 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
// Utils
|
// 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
|
@Override
|
||||||
public void setTitle(String title) {
|
public void setTitle(String title) {
|
||||||
super.setTitle(title);
|
super.setTitle(title);
|
||||||
headerTitleView.setText(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.LocalPlaylistItemHolder;
|
||||||
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder;
|
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder;
|
||||||
import org.schabi.newpipe.fragments.local.holder.LocalStatisticStreamItemHolder;
|
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.Localization;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
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_STATISTICS_HOLDER_TYPE = 0x1000;
|
||||||
private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001;
|
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 LocalItemBuilder localItemBuilder;
|
||||||
private final ArrayList<LocalItem> localItems;
|
private final ArrayList<LocalItem> localItems;
|
||||||
private final DateFormat dateFormat;
|
private final DateFormat dateFormat;
|
||||||
|
@ -187,7 +188,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
final LocalItem item = localItems.get(position);
|
final LocalItem item = localItems.get(position);
|
||||||
|
|
||||||
switch (item.getLocalItemType()) {
|
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 PLAYLIST_STREAM_ITEM: return STREAM_PLAYLIST_HOLDER_TYPE;
|
||||||
case STATISTIC_STREAM_ITEM: return STREAM_STATISTICS_HOLDER_TYPE;
|
case STATISTIC_STREAM_ITEM: return STREAM_STATISTICS_HOLDER_TYPE;
|
||||||
default:
|
default:
|
||||||
|
@ -206,8 +209,10 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
return new HeaderFooterHolder(header);
|
return new HeaderFooterHolder(header);
|
||||||
case FOOTER_TYPE:
|
case FOOTER_TYPE:
|
||||||
return new HeaderFooterHolder(footer);
|
return new HeaderFooterHolder(footer);
|
||||||
case PLAYLIST_HOLDER_TYPE:
|
case LOCAL_PLAYLIST_HOLDER_TYPE:
|
||||||
return new LocalPlaylistItemHolder(localItemBuilder, parent);
|
return new LocalPlaylistItemHolder(localItemBuilder, parent);
|
||||||
|
case REMOTE_PLAYLIST_HOLDER_TYPE:
|
||||||
|
return new RemotePlaylistItemHolder(localItemBuilder, parent);
|
||||||
case STREAM_PLAYLIST_HOLDER_TYPE:
|
case STREAM_PLAYLIST_HOLDER_TYPE:
|
||||||
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
|
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
|
||||||
case STREAM_STATISTICS_HOLDER_TYPE:
|
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.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -14,23 +14,30 @@ import org.reactivestreams.Subscriber;
|
||||||
import org.reactivestreams.Subscription;
|
import org.reactivestreams.Subscription;
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
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.PlaylistMetadataEntry;
|
||||||
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
import org.schabi.newpipe.fragments.local.BaseLocalListFragment;
|
import org.schabi.newpipe.fragments.local.BaseLocalListFragment;
|
||||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||||
|
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
|
import io.reactivex.Flowable;
|
||||||
|
import io.reactivex.Single;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
|
|
||||||
public final class BookmarkFragment
|
public final class BookmarkFragment
|
||||||
extends BaseLocalListFragment<List<PlaylistMetadataEntry>, Void> {
|
extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
|
||||||
|
|
||||||
private View watchHistoryButton;
|
private View watchHistoryButton;
|
||||||
private View mostWatchedButton;
|
private View mostWatchedButton;
|
||||||
|
@ -41,6 +48,7 @@ public final class BookmarkFragment
|
||||||
private Subscription databaseSubscription;
|
private Subscription databaseSubscription;
|
||||||
private CompositeDisposable disposables = new CompositeDisposable();
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
private LocalPlaylistManager localPlaylistManager;
|
private LocalPlaylistManager localPlaylistManager;
|
||||||
|
private RemotePlaylistManager remotePlaylistManager;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Fragment LifeCycle - Creation
|
// Fragment LifeCycle - Creation
|
||||||
|
@ -49,7 +57,9 @@ public final class BookmarkFragment
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(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();
|
disposables = new CompositeDisposable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,17 +109,28 @@ public final class BookmarkFragment
|
||||||
@Override
|
@Override
|
||||||
public void selected(LocalItem selectedItem) {
|
public void selected(LocalItem selectedItem) {
|
||||||
// Requires the parent fragment to find holder for fragment replacement
|
// 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);
|
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
|
||||||
NavigationHelper.openLocalPlaylistFragment(
|
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
|
||||||
getParentFragment().getFragmentManager(), entry.uid, entry.name);
|
entry.name);
|
||||||
|
|
||||||
|
} else if (selectedItem instanceof PlaylistRemoteEntity) {
|
||||||
|
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
|
||||||
|
NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(),
|
||||||
|
entry.getUrl(), entry.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void held(LocalItem selectedItem) {
|
public void held(LocalItem selectedItem) {
|
||||||
if (selectedItem instanceof PlaylistMetadataEntry) {
|
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
|
@Override
|
||||||
public void startLoading(boolean forceLoad) {
|
public void startLoading(boolean forceLoad) {
|
||||||
super.startLoading(forceLoad);
|
super.startLoading(forceLoad);
|
||||||
localPlaylistManager.getPlaylists()
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
Flowable.combineLatest(
|
||||||
.subscribe(getSubscriptionSubscriber());
|
localPlaylistManager.getPlaylists(),
|
||||||
|
remotePlaylistManager.getPlaylists(),
|
||||||
|
BookmarkFragment::merge
|
||||||
|
).onBackpressureLatest()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getPlaylistsSubscriber());
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -165,6 +191,7 @@ public final class BookmarkFragment
|
||||||
|
|
||||||
disposables = null;
|
disposables = null;
|
||||||
localPlaylistManager = null;
|
localPlaylistManager = null;
|
||||||
|
remotePlaylistManager = null;
|
||||||
itemsListState = null;
|
itemsListState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,8 +199,8 @@ public final class BookmarkFragment
|
||||||
// Subscriptions Loader
|
// Subscriptions Loader
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Subscriber<List<PlaylistMetadataEntry>> getSubscriptionSubscriber() {
|
private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() {
|
||||||
return new Subscriber<List<PlaylistMetadataEntry>>() {
|
return new Subscriber<List<PlaylistLocalItem>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Subscription s) {
|
public void onSubscribe(Subscription s) {
|
||||||
showLoading();
|
showLoading();
|
||||||
|
@ -183,7 +210,7 @@ public final class BookmarkFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNext(List<PlaylistMetadataEntry> subscriptions) {
|
public void onNext(List<PlaylistLocalItem> subscriptions) {
|
||||||
handleResult(subscriptions);
|
handleResult(subscriptions);
|
||||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||||
}
|
}
|
||||||
|
@ -200,7 +227,7 @@ public final class BookmarkFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleResult(@NonNull List<PlaylistMetadataEntry> result) {
|
public void handleResult(@NonNull List<PlaylistLocalItem> result) {
|
||||||
super.handleResult(result);
|
super.handleResult(result);
|
||||||
|
|
||||||
itemListAdapter.clearStreamItemList();
|
itemListAdapter.clearStreamItemList();
|
||||||
|
@ -240,25 +267,41 @@ public final class BookmarkFragment
|
||||||
// Utils
|
// 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)
|
new AlertDialog.Builder(activity)
|
||||||
.setTitle(item.name)
|
.setTitle(name)
|
||||||
.setMessage(R.string.delete_playlist_prompt)
|
.setMessage(R.string.delete_playlist_prompt)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.delete, (dialog, i) ->
|
.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)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Disposable deletePlaylist(final long playlistId) {
|
private static List<PlaylistLocalItem> merge(final List<PlaylistMetadataEntry> localPlaylists,
|
||||||
return localPlaylistManager.deletePlaylist(playlistId)
|
final List<PlaylistRemoteEntity> remotePlaylists) {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
List<PlaylistLocalItem> items = new ArrayList<>(
|
||||||
.subscribe(ignored -> {/*Do nothing on success*/},
|
localPlaylists.size() + remotePlaylists.size());
|
||||||
throwable -> Log.e(TAG, "Playlist deletion failed, id=["
|
items.addAll(localPlaylists);
|
||||||
+ playlistId + "]")
|
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;
|
import java.text.DateFormat;
|
||||||
|
|
||||||
public class LocalPlaylistItemHolder extends LocalItemHolder {
|
public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
||||||
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 LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
public LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||||
this(infoItemBuilder, R.layout.list_playlist_mini_item, parent);
|
super(infoItemBuilder, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,29 +31,6 @@ public class LocalPlaylistItemHolder extends LocalItemHolder {
|
||||||
|
|
||||||
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, DISPLAY_THUMBNAIL_OPTIONS);
|
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, DISPLAY_THUMBNAIL_OPTIONS);
|
||||||
|
|
||||||
itemView.setOnClickListener(view -> {
|
super.updateFromItem(localItem, dateFormat);
|
||||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
|
||||||
itemBuilder.getOnItemSelectedListener().selected(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
itemView.setLongClickable(true);
|
|
||||||
itemView.setOnLongClickListener(view -> {
|
|
||||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
|
||||||
itemBuilder.getOnItemSelectedListener().held(item);
|
|
||||||
}
|
|
||||||
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,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:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:contentDescription="@string/append_playlist"
|
android:contentDescription="@string/append_playlist"
|
||||||
android:drawableTop="?attr/playlist_add"
|
android:drawableTop="?attr/ic_playlist_add"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingBottom="6dp"
|
android:paddingBottom="6dp"
|
||||||
android:paddingTop="6dp"
|
android:paddingTop="6dp"
|
||||||
|
|
|
@ -22,12 +22,4 @@
|
||||||
android:icon="?attr/share"
|
android:icon="?attr/share"
|
||||||
android:title="@string/share"
|
android:title="@string/share"
|
||||||
app:showAsAction="ifRoom"/>
|
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>
|
</menu>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_append_playlist"
|
android:id="@+id/action_append_playlist"
|
||||||
android:icon="?attr/playlist_add"
|
android:icon="?attr/ic_playlist_add"
|
||||||
android:title="@string/append_playlist"
|
android:title="@string/append_playlist"
|
||||||
android:visible="true"
|
android:visible="true"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
|
@ -15,10 +15,18 @@
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_append_playlist"
|
android:id="@+id/menu_item_bookmark"
|
||||||
android:icon="?attr/playlist_add"
|
android:icon="?attr/ic_playlist_add"
|
||||||
android:title="@string/append_playlist"
|
android:title="@string/bookmark_playlist"
|
||||||
android:visible="false"
|
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"/>
|
tools:visible="true"/>
|
||||||
</menu>
|
</menu>
|
|
@ -27,7 +27,8 @@
|
||||||
<attr name="ic_hot" format="reference"/>
|
<attr name="ic_hot" format="reference"/>
|
||||||
<attr name="ic_channel" format="reference"/>
|
<attr name="ic_channel" format="reference"/>
|
||||||
<attr name="ic_bookmark" 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-->
|
<!-- Can't refer to colors directly into drawable's xml-->
|
||||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||||
|
|
|
@ -387,6 +387,9 @@
|
||||||
<string name="append_playlist">Add To Playlist</string>
|
<string name="append_playlist">Add To Playlist</string>
|
||||||
<string name="set_as_playlist_thumbnail">Set as Playlist Thumbnail</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="delete_playlist_prompt">Do you want to delete this playlist?</string>
|
||||||
<string name="playlist_creation_success">Playlist successfully created</string>
|
<string name="playlist_creation_success">Playlist successfully created</string>
|
||||||
<string name="playlist_add_stream_success">Added to playlist</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_hot">@drawable/ic_whatshot_black_24dp</item>
|
||||||
<item name="ic_channel">@drawable/ic_channel_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="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="separator_color">@color/light_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/light_contrast_background_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_hot">@drawable/ic_whatshot_white_24dp</item>
|
||||||
<item name="ic_channel">@drawable/ic_channel_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="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="separator_color">@color/dark_separator_color</item>
|
||||||
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||||
|
|