From dfc27b2480ae8b267ca7882ad001230619cb4534 Mon Sep 17 00:00:00 2001 From: Roy Yosef Date: Thu, 30 Apr 2020 23:52:47 +0300 Subject: [PATCH 1/4] Add playlist tab to main page Add bookmarked playlist as tab in the main page (by Settings > Content > Content of main page) --- .../local/bookmark/BookmarkFragment.java | 30 +-- .../settings/SelectPlaylistFragment.java | 240 ++++++++++++++++++ .../settings/tabs/ChooseTabsFragment.java | 30 +++ .../org/schabi/newpipe/settings/tabs/Tab.java | 141 +++++++++- .../newpipe/util/PlaylistItemsUtils.java | 38 +++ .../res/layout/select_playlist_fragment.xml | 43 ++++ app/src/main/res/values/strings.xml | 3 + 7 files changed, 495 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java create mode 100644 app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java create mode 100644 app/src/main/res/layout/select_playlist_fragment.xml 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..7485a73ef 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 @@ -29,9 +29,8 @@ import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; +import org.schabi.newpipe.util.PlaylistItemsUtils; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import icepick.State; @@ -54,31 +53,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 +138,7 @@ public final class BookmarkFragment extends BaseLocalListFragment playlists = new Vector<>(); + + public void setOnSelectedLisener(final OnSelectedLisener listener) { + onSelectedLisener = listener; + } + + public void setOnCancelListener(final OnCancelListener listener) { + onCancelListener = listener; + } + + /*////////////////////////////////////////////////////////////////////////// + // Init + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + 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(this.getContext()); + LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); + RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); + + Flowable.combineLatest(localPlaylistManager.getPlaylists(), + remotePlaylistManager.getPlaylists(), PlaylistItemsUtils::merge) + .toObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getPlaylistsObserver()); + + return v; + } + + /*////////////////////////////////////////////////////////////////////////// + // Handle actions + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCancel(final DialogInterface dialogInterface) { + super.onCancel(dialogInterface); + if (onCancelListener != null) { + onCancelListener.onCancel(); + } + } + + private void clickedItem(final int position) { + if (onSelectedLisener != null) { + LocalItem selectedItem = playlists.get(position); + + if (selectedItem instanceof PlaylistMetadataEntry) { + final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + onSelectedLisener + .onLocalPlaylistSelected(entry.uid, entry.name); + + } else if (selectedItem instanceof PlaylistRemoteEntity) { + final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + onSelectedLisener.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); + + } + + private Observer> getPlaylistsObserver() { + return new Observer>() { + @Override + public void onSubscribe(final Disposable d) { } + + @Override + public void onNext(final List newPlaylists) { + displayPlaylists(newPlaylists); + } + + @Override + public void onError(final Throwable exception) { + SelectPlaylistFragment.this.onError(exception); + } + + @Override + public void onComplete() { } + }; + } + + /*////////////////////////////////////////////////////////////////////////// + // 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 OnSelectedLisener { + 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) { + 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) { + 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..ad8ee043b 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; @@ -211,6 +212,23 @@ public class ChooseTabsFragment extends Fragment { addTab(new Tab.ChannelTab(serviceId, url, name))); selectChannelFragment.show(requireFragmentManager(), "select_channel"); return; + case PLAYLIST: + SelectPlaylistFragment selectPlaylistFragment = new SelectPlaylistFragment(); + selectPlaylistFragment.setOnSelectedLisener( + new SelectPlaylistFragment.OnSelectedLisener() { + @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)); @@ -393,6 +416,13 @@ public class ChooseTabsFragment extends Fragment { tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab) .getChannelServiceId()) + "/" + tab.getTabName(requireContext()); break; + case PLAYLIST: + int serviceId = ((Tab.PlaylistTab) tab).getPlaylistServiceId(); + 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..28a4e2723 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,134 @@ 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.playlistName = ""; + this.playlistId = -1; + this.playlistType = LocalItemType.PLAYLIST_LOCAL_ITEM; + this.playlistServiceId = -1; + this.playlistUrl = ""; + } + + 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_list); + } + + @Override + public Fragment getFragment(final Context context) { + if (playlistType == LocalItemType.PLAYLIST_LOCAL_ITEM) { + return LocalPlaylistFragment.getInstance(playlistId, + playlistName == null ? "" : playlistName); + + } else { // playlistType == LocalItemType.PLAYLIST_REMOTE_ITEM + return PlaylistFragment.getInstance(playlistServiceId, playlistUrl, + playlistName == null ? "" : 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) { + boolean baseEqual = super.equals(obj) + && Objects.equals(playlistType, ((PlaylistTab) obj).playlistType) + && Objects.equals(playlistName, ((PlaylistTab) obj).playlistName); + + if (!baseEqual) { + return false; + } + + boolean localPlaylistEquals = playlistId == ((PlaylistTab) obj).playlistId; + boolean remotePlaylistEquals = + playlistServiceId == ((PlaylistTab) obj).playlistServiceId + && Objects.equals(playlistUrl, ((PlaylistTab) obj).playlistUrl); + + return localPlaylistEquals || remotePlaylistEquals; + } + + 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/java/org/schabi/newpipe/util/PlaylistItemsUtils.java b/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java new file mode 100644 index 000000000..230be4d28 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java @@ -0,0 +1,38 @@ +package org.schabi.newpipe.util; + +import org.schabi.newpipe.database.playlist.PlaylistLocalItem; +import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; +import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class PlaylistItemsUtils { + private PlaylistItemsUtils() { } + + public static List 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; + } +} 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..14462662a --- /dev/null +++ b/app/src/main/res/layout/select_playlist_fragment.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a4dfd255d..79b86d2ad 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 From 13c0fdef082e2205b1e7345e856ecdde23d664a0 Mon Sep 17 00:00:00 2001 From: Roy Yosef Date: Sun, 3 May 2020 00:14:31 +0300 Subject: [PATCH 2/4] Final declarations, naming & redundant code * add final declarations where missing * fix typo "onSelectedLisener" to "onSelectedListener" * rename "baseEqual" to "baseEquals" * replace getPlaylistsObserver code with functions pointers * remove duplicate code in constructors * remove useless null checks --- .../settings/SelectChannelFragment.java | 12 ++-- .../newpipe/settings/SelectKioskFragment.java | 12 ++-- .../settings/SelectPlaylistFragment.java | 70 ++++++++----------- .../settings/tabs/ChooseTabsFragment.java | 22 +++--- .../org/schabi/newpipe/settings/tabs/Tab.java | 22 +++--- .../newpipe/util/PlaylistItemsUtils.java | 2 +- .../res/layout/select_playlist_fragment.xml | 6 +- 7 files changed, 64 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 5b452430b..df529fee0 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -64,7 +64,7 @@ public class SelectChannelFragment extends DialogFragment { private final ImageLoader imageLoader = ImageLoader.getInstance(); - private OnSelectedLisener onSelectedLisener = null; + private OnSelectedListener onSelectedListener = null; private OnCancelListener onCancelListener = null; private ProgressBar progressBar; @@ -73,8 +73,8 @@ public class SelectChannelFragment extends DialogFragment { private List 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 index dc0b173ed..795ced5fa 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -35,10 +35,7 @@ import java.util.List; import java.util.Vector; import io.reactivex.Flowable; -import io.reactivex.Observer; -import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; public class SelectPlaylistFragment extends DialogFragment { /** @@ -49,17 +46,18 @@ public class SelectPlaylistFragment extends DialogFragment { private final ImageLoader imageLoader = ImageLoader.getInstance(); - private OnSelectedLisener onSelectedLisener = null; + 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 setOnSelectedLisener(final OnSelectedLisener listener) { - onSelectedLisener = listener; + public void setOnSelectedListener(final OnSelectedListener listener) { + onSelectedListener = listener; } public void setOnCancelListener(final OnCancelListener listener) { @@ -67,13 +65,14 @@ public class SelectPlaylistFragment extends DialogFragment { } /*////////////////////////////////////////////////////////////////////////// - // Init + // Fragment's Lifecycle //////////////////////////////////////////////////////////////////////////*/ @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.select_playlist_fragment, container, false); + 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(); @@ -86,19 +85,26 @@ public class SelectPlaylistFragment extends DialogFragment { emptyView.setVisibility(View.GONE); final AppDatabase database = NewPipeDatabase.getInstance(this.getContext()); - LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); - RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); + final LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); + final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); - Flowable.combineLatest(localPlaylistManager.getPlaylists(), + playlistsSubscriber = Flowable.combineLatest(localPlaylistManager.getPlaylists(), remotePlaylistManager.getPlaylists(), PlaylistItemsUtils::merge) - .toObservable() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(getPlaylistsObserver()); + .subscribe(this::displayPlaylists, this::onError); return v; } + @Override + public void onDestroy() { + super.onDestroy(); + + if (playlistsSubscriber != null) { + playlistsSubscriber.dispose(); + playlistsSubscriber = null; + } + } + /*////////////////////////////////////////////////////////////////////////// // Handle actions //////////////////////////////////////////////////////////////////////////*/ @@ -112,17 +118,17 @@ public class SelectPlaylistFragment extends DialogFragment { } private void clickedItem(final int position) { - if (onSelectedLisener != null) { - LocalItem selectedItem = playlists.get(position); + if (onSelectedListener != null) { + final LocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - onSelectedLisener + onSelectedListener .onLocalPlaylistSelected(entry.uid, entry.name); } else if (selectedItem instanceof PlaylistRemoteEntity) { final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); - onSelectedLisener.onRemotePlaylistSelected( + onSelectedListener.onRemotePlaylistSelected( entry.getServiceId(), entry.getUrl(), entry.getName()); } } @@ -144,26 +150,6 @@ public class SelectPlaylistFragment extends DialogFragment { } - private Observer> getPlaylistsObserver() { - return new Observer>() { - @Override - public void onSubscribe(final Disposable d) { } - - @Override - public void onNext(final List newPlaylists) { - displayPlaylists(newPlaylists); - } - - @Override - public void onError(final Throwable exception) { - SelectPlaylistFragment.this.onError(exception); - } - - @Override - public void onComplete() { } - }; - } - /*////////////////////////////////////////////////////////////////////////// // Error //////////////////////////////////////////////////////////////////////////*/ @@ -178,7 +164,7 @@ public class SelectPlaylistFragment extends DialogFragment { // Interfaces //////////////////////////////////////////////////////////////////////////*/ - public interface OnSelectedLisener { + public interface OnSelectedListener { void onLocalPlaylistSelected(long id, String name); void onRemotePlaylistSelected(int serviceId, String url, String name); } @@ -192,14 +178,14 @@ public class SelectPlaylistFragment extends DialogFragment { @Override public SelectPlaylistItemHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { - View item = LayoutInflater.from(parent.getContext()) + 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) { - PlaylistLocalItem selectedItem = playlists.get(position); + final PlaylistLocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); 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 ad8ee043b..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 @@ -49,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; /*////////////////////////////////////////////////////////////////////////// @@ -79,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); @@ -139,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); } @@ -202,20 +202,20 @@ 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.setOnSelectedLisener( - new SelectPlaylistFragment.OnSelectedLisener() { + selectPlaylistFragment.setOnSelectedListener( + new SelectPlaylistFragment.OnSelectedListener() { @Override public void onLocalPlaylistSelected(final long id, final String name) { addTab(new Tab.PlaylistTab(id, name)); @@ -360,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); } @@ -417,8 +417,8 @@ public class ChooseTabsFragment extends Fragment { .getChannelServiceId()) + "/" + tab.getTabName(requireContext()); break; case PLAYLIST: - int serviceId = ((Tab.PlaylistTab) tab).getPlaylistServiceId(); - String serviceName = serviceId == -1 + final int serviceId = ((Tab.PlaylistTab) tab).getPlaylistServiceId(); + final String serviceName = serviceId == -1 ? getString(R.string.local) : NewPipe.getNameOfService(serviceId); tabName = serviceName + "/" + tab.getTabName(requireContext()); 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 28a4e2723..85de8462d 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 @@ -504,11 +504,7 @@ public abstract class Tab { private LocalItemType playlistType; private PlaylistTab() { - this.playlistName = ""; - this.playlistId = -1; - this.playlistType = LocalItemType.PLAYLIST_LOCAL_ITEM; - this.playlistServiceId = -1; - this.playlistUrl = ""; + this(-1, ""); } public PlaylistTab(final long playlistId, final String playlistName) { @@ -545,18 +541,16 @@ public abstract class Tab { @DrawableRes @Override public int getTabIconRes(final Context context) { - return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_list); + 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 == null ? "" : playlistName); + return LocalPlaylistFragment.getInstance(playlistId, playlistName); } else { // playlistType == LocalItemType.PLAYLIST_REMOTE_ITEM - return PlaylistFragment.getInstance(playlistServiceId, playlistUrl, - playlistName == null ? "" : playlistName); + return PlaylistFragment.getInstance(playlistServiceId, playlistUrl, playlistName); } } @@ -583,16 +577,16 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - boolean baseEqual = super.equals(obj) + final boolean baseEquals = super.equals(obj) && Objects.equals(playlistType, ((PlaylistTab) obj).playlistType) && Objects.equals(playlistName, ((PlaylistTab) obj).playlistName); - if (!baseEqual) { + if (!baseEquals) { return false; } - boolean localPlaylistEquals = playlistId == ((PlaylistTab) obj).playlistId; - boolean remotePlaylistEquals = + final boolean localPlaylistEquals = playlistId == ((PlaylistTab) obj).playlistId; + final boolean remotePlaylistEquals = playlistServiceId == ((PlaylistTab) obj).playlistServiceId && Objects.equals(playlistUrl, ((PlaylistTab) obj).playlistUrl); diff --git a/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java b/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java index 230be4d28..e5309210c 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java @@ -14,7 +14,7 @@ public final class PlaylistItemsUtils { public static List merge( final List localPlaylists, final List remotePlaylists) { - List items = new ArrayList<>( + final List items = new ArrayList<>( localPlaylists.size() + remotePlaylists.size()); items.addAll(localPlaylists); items.addAll(remotePlaylists); diff --git a/app/src/main/res/layout/select_playlist_fragment.xml b/app/src/main/res/layout/select_playlist_fragment.xml index 14462662a..ca0d49e32 100644 --- a/app/src/main/res/layout/select_playlist_fragment.xml +++ b/app/src/main/res/layout/select_playlist_fragment.xml @@ -23,7 +23,9 @@ android:id="@+id/items_list" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:listitem="@layout/list_playlist_mini_item"> + tools:listitem="@layout/list_playlist_mini_item"> + + - \ No newline at end of file + From 248212588dfe6ad14814a92f8e17516a427635cc Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 28 Jun 2020 22:55:18 +0200 Subject: [PATCH 3/4] Fix style issues --- .../org/schabi/newpipe/settings/tabs/Tab.java | 17 ++++++----------- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) 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 85de8462d..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 @@ -577,20 +577,15 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - final boolean baseEquals = super.equals(obj) + if (!(super.equals(obj) && Objects.equals(playlistType, ((PlaylistTab) obj).playlistType) - && Objects.equals(playlistName, ((PlaylistTab) obj).playlistName); - - if (!baseEquals) { - return false; + && Objects.equals(playlistName, ((PlaylistTab) obj).playlistName))) { + return false; // base objects are different } - final boolean localPlaylistEquals = playlistId == ((PlaylistTab) obj).playlistId; - final boolean remotePlaylistEquals = - playlistServiceId == ((PlaylistTab) obj).playlistServiceId - && Objects.equals(playlistUrl, ((PlaylistTab) obj).playlistUrl); - - return localPlaylistEquals || remotePlaylistEquals; + return (playlistId == ((PlaylistTab) obj).playlistId) // local + || (playlistServiceId == ((PlaylistTab) obj).playlistServiceId // remote + && Objects.equals(playlistUrl, ((PlaylistTab) obj).playlistUrl)); } public int getPlaylistServiceId() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 79b86d2ad..79624032b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -653,5 +653,5 @@ Channel\'s avatar thumbnail Created by %s By %s - Playlist Page + Playlist page From 8cc21920b75c0744f31cb8f09d1d97e69c6c80bd Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 4 Jul 2020 11:31:24 +0200 Subject: [PATCH 4/4] Move local/remote playlist merge() to PlaylistLocalItem class In order not to have a utils class just for one function --- .../database/playlist/PlaylistLocalItem.java | 26 +++++++++++++ .../local/bookmark/BookmarkFragment.java | 3 +- .../settings/SelectPlaylistFragment.java | 5 +-- .../newpipe/util/PlaylistItemsUtils.java | 38 ------------------- 4 files changed, 29 insertions(+), 43 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/util/PlaylistItemsUtils.java 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 7485a73ef..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 @@ -29,7 +29,6 @@ import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.util.PlaylistItemsUtils; import java.util.List; @@ -138,7 +137,7 @@ public final class BookmarkFragment extends BaseLocalListFragment 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) -> { - 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; - } -}