diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java index fd99f84a1..3ce95631c 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java @@ -1,7 +1,33 @@ package org.schabi.newpipe.database.playlist; import org.schabi.newpipe.database.LocalItem; +import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public interface PlaylistLocalItem extends LocalItem { String getOrderingName(); + + static List merge( + final List localPlaylists, + final List remotePlaylists) { + final List items = new ArrayList<>( + localPlaylists.size() + remotePlaylists.size()); + items.addAll(localPlaylists); + items.addAll(remotePlaylists); + + Collections.sort(items, (left, right) -> { + final String on1 = left.getOrderingName(); + final String on2 = right.getOrderingName(); + if (on1 == null) { + return on2 == null ? 0 : 1; + } else { + return on2 == null ? -1 : on1.compareToIgnoreCase(on2); + } + }); + + return items; + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index d99a05976..7e11d7a2e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -30,8 +30,6 @@ 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; @@ -54,31 +52,6 @@ public final class BookmarkFragment extends BaseLocalListFragment merge( - final List localPlaylists, - final List remotePlaylists) { - List items = new ArrayList<>( - localPlaylists.size() + remotePlaylists.size()); - items.addAll(localPlaylists); - items.addAll(remotePlaylists); - - Collections.sort(items, (left, right) -> { - String on1 = left.getOrderingName(); - String on2 = right.getOrderingName(); - if (on1 == null && on2 == null) { - return 0; - } else if (on1 != null && on2 == null) { - return -1; - } else if (on1 == null && on2 != null) { - return 1; - } else { - return on1.compareToIgnoreCase(on2); - } - }); - - return items; - } - @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -164,7 +137,7 @@ public final class BookmarkFragment extends BaseLocalListFragment subscriptions = new Vector<>(); - public void setOnSelectedLisener(final OnSelectedLisener listener) { - onSelectedLisener = listener; + public void setOnSelectedListener(final OnSelectedListener listener) { + onSelectedListener = listener; } public void setOnCancelListener(final OnCancelListener listener) { @@ -129,9 +129,9 @@ public class SelectChannelFragment extends DialogFragment { } private void clickedItem(final int position) { - if (onSelectedLisener != null) { + if (onSelectedListener != null) { SubscriptionEntity entry = subscriptions.get(position); - onSelectedLisener + onSelectedListener .onChannelSelected(entry.getServiceId(), entry.getUrl(), entry.getName()); } dismiss(); @@ -186,7 +186,7 @@ public class SelectChannelFragment extends DialogFragment { // Interfaces //////////////////////////////////////////////////////////////////////////*/ - public interface OnSelectedLisener { + public interface OnSelectedListener { void onChannelSelected(int serviceId, String url, String name); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 4df70ccec..13d34dec8 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -52,11 +52,11 @@ public class SelectKioskFragment extends DialogFragment { private RecyclerView recyclerView = null; private SelectKioskAdapter selectKioskAdapter = null; - private OnSelectedLisener onSelectedLisener = null; + private OnSelectedListener onSelectedListener = null; private OnCancelListener onCancelListener = null; - public void setOnSelectedLisener(final OnSelectedLisener listener) { - onSelectedLisener = listener; + public void setOnSelectedListener(final OnSelectedListener listener) { + onSelectedListener = listener; } public void setOnCancelListener(final OnCancelListener listener) { @@ -102,8 +102,8 @@ public class SelectKioskFragment extends DialogFragment { } private void clickedItem(final SelectKioskAdapter.Entry entry) { - if (onSelectedLisener != null) { - onSelectedLisener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName); + if (onSelectedListener != null) { + onSelectedListener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName); } dismiss(); } @@ -122,7 +122,7 @@ public class SelectKioskFragment extends DialogFragment { // Interfaces //////////////////////////////////////////////////////////////////////////*/ - public interface OnSelectedLisener { + public interface OnSelectedListener { void onKioskSelected(int serviceId, String kioskId, String kioskName); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java new file mode 100644 index 000000000..1d5c94421 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -0,0 +1,225 @@ +package org.schabi.newpipe.settings; + +import android.app.Activity; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; + +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.local.playlist.LocalPlaylistManager; +import org.schabi.newpipe.local.playlist.RemotePlaylistManager; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; + +import java.util.List; +import java.util.Vector; + +import io.reactivex.Flowable; +import io.reactivex.disposables.Disposable; + +public class SelectPlaylistFragment extends DialogFragment { + /** + * This contains the base display options for images. + */ + private static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS + = new DisplayImageOptions.Builder().cacheInMemory(true).build(); + + private final ImageLoader imageLoader = ImageLoader.getInstance(); + + private OnSelectedListener onSelectedListener = null; + private OnCancelListener onCancelListener = null; + + private ProgressBar progressBar; + private TextView emptyView; + private RecyclerView recyclerView; + private Disposable playlistsSubscriber; + + private List playlists = new Vector<>(); + + public void setOnSelectedListener(final OnSelectedListener listener) { + onSelectedListener = listener; + } + + public void setOnCancelListener(final OnCancelListener listener) { + onCancelListener = listener; + } + + /*////////////////////////////////////////////////////////////////////////// + // Fragment's Lifecycle + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + final View v = + inflater.inflate(R.layout.select_playlist_fragment, container, false); + recyclerView = v.findViewById(R.id.items_list); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter(); + recyclerView.setAdapter(playlistAdapter); + + progressBar = v.findViewById(R.id.progressBar); + emptyView = v.findViewById(R.id.empty_state_view); + progressBar.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + emptyView.setVisibility(View.GONE); + + final AppDatabase database = NewPipeDatabase.getInstance(requireContext()); + final LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); + final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); + + playlistsSubscriber = Flowable.combineLatest(localPlaylistManager.getPlaylists(), + remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) + .subscribe(this::displayPlaylists, this::onError); + + return v; + } + + @Override + public void onDestroy() { + super.onDestroy(); + + if (playlistsSubscriber != null) { + playlistsSubscriber.dispose(); + playlistsSubscriber = null; + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Handle actions + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCancel(final DialogInterface dialogInterface) { + super.onCancel(dialogInterface); + if (onCancelListener != null) { + onCancelListener.onCancel(); + } + } + + private void clickedItem(final int position) { + if (onSelectedListener != null) { + final LocalItem selectedItem = playlists.get(position); + + if (selectedItem instanceof PlaylistMetadataEntry) { + final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + onSelectedListener + .onLocalPlaylistSelected(entry.uid, entry.name); + + } else if (selectedItem instanceof PlaylistRemoteEntity) { + final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + onSelectedListener.onRemotePlaylistSelected( + entry.getServiceId(), entry.getUrl(), entry.getName()); + } + } + dismiss(); + } + + /*////////////////////////////////////////////////////////////////////////// + // Item handling + //////////////////////////////////////////////////////////////////////////*/ + + private void displayPlaylists(final List newPlaylists) { + this.playlists = newPlaylists; + progressBar.setVisibility(View.GONE); + if (newPlaylists.isEmpty()) { + emptyView.setVisibility(View.VISIBLE); + return; + } + recyclerView.setVisibility(View.VISIBLE); + + } + + /*////////////////////////////////////////////////////////////////////////// + // Error + //////////////////////////////////////////////////////////////////////////*/ + + protected void onError(final Throwable e) { + final Activity activity = getActivity(); + ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo + .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); + } + + /*////////////////////////////////////////////////////////////////////////// + // Interfaces + //////////////////////////////////////////////////////////////////////////*/ + + public interface OnSelectedListener { + void onLocalPlaylistSelected(long id, String name); + void onRemotePlaylistSelected(int serviceId, String url, String name); + } + + public interface OnCancelListener { + void onCancel(); + } + + private class SelectPlaylistAdapter + extends RecyclerView.Adapter { + @Override + public SelectPlaylistItemHolder onCreateViewHolder(final ViewGroup parent, + final int viewType) { + final View item = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.list_playlist_mini_item, parent, false); + return new SelectPlaylistItemHolder(item); + } + + @Override + public void onBindViewHolder(final SelectPlaylistItemHolder holder, final int position) { + final PlaylistLocalItem selectedItem = playlists.get(position); + + if (selectedItem instanceof PlaylistMetadataEntry) { + final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + + holder.titleView.setText(entry.name); + holder.view.setOnClickListener(view -> clickedItem(position)); + imageLoader.displayImage(entry.thumbnailUrl, holder.thumbnailView, + DISPLAY_IMAGE_OPTIONS); + + } else if (selectedItem instanceof PlaylistRemoteEntity) { + final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + + holder.titleView.setText(entry.getName()); + holder.view.setOnClickListener(view -> clickedItem(position)); + imageLoader.displayImage(entry.getThumbnailUrl(), holder.thumbnailView, + DISPLAY_IMAGE_OPTIONS); + } + } + + @Override + public int getItemCount() { + return playlists.size(); + } + + public class SelectPlaylistItemHolder extends RecyclerView.ViewHolder { + public final View view; + final ImageView thumbnailView; + final TextView titleView; + + SelectPlaylistItemHolder(final View v) { + super(v); + this.view = v; + thumbnailView = v.findViewById(R.id.itemThumbnailView); + titleView = v.findViewById(R.id.itemTitleView); + } + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index 6ebfbd73c..1b26cd529 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -34,6 +34,7 @@ import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.settings.SelectChannelFragment; import org.schabi.newpipe.settings.SelectKioskFragment; +import org.schabi.newpipe.settings.SelectPlaylistFragment; import org.schabi.newpipe.settings.tabs.AddTabDialog.ChooseTabListItem; import org.schabi.newpipe.util.ThemeHelper; @@ -48,7 +49,7 @@ public class ChooseTabsFragment extends Fragment { private TabsManager tabsManager; - private List tabList = new ArrayList<>(); + private final List tabList = new ArrayList<>(); private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter; /*////////////////////////////////////////////////////////////////////////// @@ -78,10 +79,10 @@ public class ChooseTabsFragment extends Fragment { initButton(rootView); - RecyclerView listSelectedTabs = rootView.findViewById(R.id.selectedTabs); + final RecyclerView listSelectedTabs = rootView.findViewById(R.id.selectedTabs); listSelectedTabs.setLayoutManager(new LinearLayoutManager(requireContext())); - ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); + final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); itemTouchHelper.attachToRecyclerView(listSelectedTabs); selectedTabsAdapter = new SelectedTabsAdapter(requireContext(), itemTouchHelper); @@ -138,7 +139,7 @@ public class ChooseTabsFragment extends Fragment { private void updateTitle() { if (getActivity() instanceof AppCompatActivity) { - ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(R.string.main_page_content); } @@ -201,16 +202,33 @@ public class ChooseTabsFragment extends Fragment { switch (type) { case KIOSK: SelectKioskFragment selectKioskFragment = new SelectKioskFragment(); - selectKioskFragment.setOnSelectedLisener((serviceId, kioskId, kioskName) -> + selectKioskFragment.setOnSelectedListener((serviceId, kioskId, kioskName) -> addTab(new Tab.KioskTab(serviceId, kioskId))); selectKioskFragment.show(requireFragmentManager(), "select_kiosk"); return; case CHANNEL: SelectChannelFragment selectChannelFragment = new SelectChannelFragment(); - selectChannelFragment.setOnSelectedLisener((serviceId, url, name) -> + selectChannelFragment.setOnSelectedListener((serviceId, url, name) -> addTab(new Tab.ChannelTab(serviceId, url, name))); selectChannelFragment.show(requireFragmentManager(), "select_channel"); return; + case PLAYLIST: + SelectPlaylistFragment selectPlaylistFragment = new SelectPlaylistFragment(); + selectPlaylistFragment.setOnSelectedListener( + new SelectPlaylistFragment.OnSelectedListener() { + @Override + public void onLocalPlaylistSelected(final long id, final String name) { + addTab(new Tab.PlaylistTab(id, name)); + } + + @Override + public void onRemotePlaylistSelected( + final int serviceId, final String url, final String name) { + addTab(new Tab.PlaylistTab(serviceId, url, name)); + } + }); + selectPlaylistFragment.show(requireFragmentManager(), "select_playlist"); + return; default: addTab(type.getTab()); break; @@ -248,6 +266,11 @@ public class ChooseTabsFragment extends Fragment { R.attr.ic_kiosk_hot))); } break; + case PLAYLIST: + returnList.add(new ChooseTabListItem(tab.getTabId(), + getString(R.string.playlist_page_summary), + tab.getTabIconRes(context))); + break; default: if (!tabList.contains(tab)) { returnList.add(new ChooseTabListItem(context, tab)); @@ -337,7 +360,7 @@ public class ChooseTabsFragment extends Fragment { @Override public ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder( @NonNull final ViewGroup parent, final int viewType) { - View view = inflater.inflate(R.layout.list_choose_tabs, parent, false); + final View view = inflater.inflate(R.layout.list_choose_tabs, parent, false); return new ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder(view); } @@ -393,6 +416,13 @@ public class ChooseTabsFragment extends Fragment { tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab) .getChannelServiceId()) + "/" + tab.getTabName(requireContext()); break; + case PLAYLIST: + final int serviceId = ((Tab.PlaylistTab) tab).getPlaylistServiceId(); + final String serviceName = serviceId == -1 + ? getString(R.string.local) + : NewPipe.getNameOfService(serviceId); + tabName = serviceName + "/" + tab.getTabName(requireContext()); + break; default: tabName = tab.getTabName(requireContext()); break; diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java index d06b4b14e..b0511cd11 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java @@ -11,6 +11,7 @@ import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonSink; import org.schabi.newpipe.R; +import org.schabi.newpipe.database.LocalItem.LocalItemType; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -18,9 +19,11 @@ import org.schabi.newpipe.fragments.BlankFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.kiosk.DefaultKioskFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; +import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.local.bookmark.BookmarkFragment; import org.schabi.newpipe.local.feed.FeedFragment; import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; +import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; import org.schabi.newpipe.local.subscription.SubscriptionFragment; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; @@ -33,7 +36,8 @@ import java.util.Objects; public abstract class Tab { private static final String JSON_TAB_ID_KEY = "tab_id"; - Tab() { } + Tab() { + } Tab(@NonNull final JsonObject jsonObject) { readDataFromJson(jsonObject); @@ -83,6 +87,8 @@ public abstract class Tab { return new KioskTab(jsonObject); case CHANNEL: return new ChannelTab(jsonObject); + case PLAYLIST: + return new PlaylistTab(jsonObject); } } @@ -147,7 +153,8 @@ public abstract class Tab { BOOKMARKS(new BookmarksTab()), HISTORY(new HistoryTab()), KIOSK(new KioskTab()), - CHANNEL(new ChannelTab()); + CHANNEL(new ChannelTab()), + PLAYLIST(new PlaylistTab()); private Tab tab; @@ -482,4 +489,123 @@ public abstract class Tab { return kioskId; } } + + public static class PlaylistTab extends Tab { + public static final int ID = 8; + private static final String JSON_PLAYLIST_SERVICE_ID_KEY = "playlist_service_id"; + private static final String JSON_PLAYLIST_URL_KEY = "playlist_url"; + private static final String JSON_PLAYLIST_NAME_KEY = "playlist_name"; + private static final String JSON_PLAYLIST_ID_KEY = "playlist_id"; + private static final String JSON_PLAYLIST_TYPE_KEY = "playlist_type"; + private int playlistServiceId; + private String playlistUrl; + private String playlistName; + private long playlistId; + private LocalItemType playlistType; + + private PlaylistTab() { + this(-1, ""); + } + + public PlaylistTab(final long playlistId, final String playlistName) { + this.playlistName = playlistName; + this.playlistId = playlistId; + this.playlistType = LocalItemType.PLAYLIST_LOCAL_ITEM; + this.playlistServiceId = -1; + this.playlistUrl = ""; + } + + public PlaylistTab(final int playlistServiceId, final String playlistUrl, + final String playlistName) { + this.playlistServiceId = playlistServiceId; + this.playlistUrl = playlistUrl; + this.playlistName = playlistName; + this.playlistType = LocalItemType.PLAYLIST_REMOTE_ITEM; + this.playlistId = -1; + } + + public PlaylistTab(final JsonObject jsonObject) { + super(jsonObject); + } + + @Override + public int getTabId() { + return ID; + } + + @Override + public String getTabName(final Context context) { + return playlistName; + } + + @DrawableRes + @Override + public int getTabIconRes(final Context context) { + return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_bookmark); + } + + @Override + public Fragment getFragment(final Context context) { + if (playlistType == LocalItemType.PLAYLIST_LOCAL_ITEM) { + return LocalPlaylistFragment.getInstance(playlistId, playlistName); + + } else { // playlistType == LocalItemType.PLAYLIST_REMOTE_ITEM + return PlaylistFragment.getInstance(playlistServiceId, playlistUrl, playlistName); + } + } + + @Override + protected void writeDataToJson(final JsonSink writerSink) { + writerSink.value(JSON_PLAYLIST_SERVICE_ID_KEY, playlistServiceId) + .value(JSON_PLAYLIST_URL_KEY, playlistUrl) + .value(JSON_PLAYLIST_NAME_KEY, playlistName) + .value(JSON_PLAYLIST_ID_KEY, playlistId) + .value(JSON_PLAYLIST_TYPE_KEY, playlistType.toString()); + } + + @Override + protected void readDataFromJson(final JsonObject jsonObject) { + playlistServiceId = jsonObject.getInt(JSON_PLAYLIST_SERVICE_ID_KEY, -1); + playlistUrl = jsonObject.getString(JSON_PLAYLIST_URL_KEY, ""); + playlistName = jsonObject.getString(JSON_PLAYLIST_NAME_KEY, ""); + playlistId = jsonObject.getInt(JSON_PLAYLIST_ID_KEY, -1); + playlistType = LocalItemType.valueOf( + jsonObject.getString(JSON_PLAYLIST_TYPE_KEY, + LocalItemType.PLAYLIST_LOCAL_ITEM.toString()) + ); + } + + @Override + public boolean equals(final Object obj) { + if (!(super.equals(obj) + && Objects.equals(playlistType, ((PlaylistTab) obj).playlistType) + && Objects.equals(playlistName, ((PlaylistTab) obj).playlistName))) { + return false; // base objects are different + } + + return (playlistId == ((PlaylistTab) obj).playlistId) // local + || (playlistServiceId == ((PlaylistTab) obj).playlistServiceId // remote + && Objects.equals(playlistUrl, ((PlaylistTab) obj).playlistUrl)); + } + + public int getPlaylistServiceId() { + return playlistServiceId; + } + + public String getPlaylistUrl() { + return playlistUrl; + } + + public String getPlaylistName() { + return playlistName; + } + + public long getPlaylistId() { + return playlistId; + } + + public LocalItemType getPlaylistType() { + return playlistType; + } + } } diff --git a/app/src/main/res/layout/select_playlist_fragment.xml b/app/src/main/res/layout/select_playlist_fragment.xml new file mode 100644 index 000000000..ca0d49e32 --- /dev/null +++ b/app/src/main/res/layout/select_playlist_fragment.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a4dfd255d..79624032b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -399,6 +399,8 @@ Channel Page Select a channel No channel subscriptions yet + Select a playlist + No playlists bookmarks yet Select a kiosk Exported Imported @@ -651,4 +653,5 @@ Channel\'s avatar thumbnail Created by %s By %s + Playlist page