From 9b4a07de345eb76c8ecc1ab944614affc48227e4 Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Sun, 28 Jan 2018 23:01:06 -0800 Subject: [PATCH] -Redone control panel in video detail fragment. -Added playlist append menu item to channel and playlist fragments. -Added debouncing to local playlist fragment to allow saving join when list is reordered or item is deleted. -Extracted hardcoded strings. --- .../fragments/detail/VideoDetailFragment.java | 1 + .../fragments/list/BaseListFragment.java | 18 ++ .../list/channel/ChannelFragment.java | 19 +- .../list/playlist/PlaylistFragment.java | 19 +- .../fragments/local/LocalItemListAdapter.java | 20 +- .../local/LocalPlaylistFragment.java | 46 ++- .../fragments/local/PlaylistAppendDialog.java | 4 +- .../local/PlaylistCreationDialog.java | 2 +- .../local/bookmark/BookmarkFragment.java | 4 +- .../bookmark/StatisticsPlaylistFragment.java | 2 +- app/src/main/res/layout/dialog_playlists.xml | 2 +- .../main/res/layout/fragment_video_detail.xml | 288 +++++++++--------- app/src/main/res/menu/menu_channel.xml | 8 + app/src/main/res/menu/menu_playlist.xml | 11 +- app/src/main/res/values/strings.xml | 7 + 15 files changed, 279 insertions(+), 172 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index b134bc98d..6907f3266 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1031,6 +1031,7 @@ public class VideoDetailFragment extends BaseStateFragment implement if (!TextUtils.isEmpty(info.getUploaderName())) { uploaderTextView.setText(info.getUploaderName()); uploaderTextView.setVisibility(View.VISIBLE); + uploaderTextView.setSelected(true); } else { uploaderTextView.setVisibility(View.GONE); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 9e4fe89ab..82b45c76e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -20,6 +20,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; +import org.schabi.newpipe.fragments.local.PlaylistAppendDialog; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.info_list.OnInfoItemGesture; @@ -27,6 +28,7 @@ import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.StateSaver; +import java.util.ArrayList; import java.util.List; import java.util.Queue; @@ -283,4 +285,20 @@ public abstract class BaseListFragment extends BaseStateFragment implem public void handleNextItems(N result) { isLoading.set(false); } + + /*////////////////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////////////////*/ + + protected void appendToPlaylist(final android.support.v4.app.FragmentManager manager, + final String tag) { + if (infoListAdapter == null) return; + List streams = new ArrayList<>(); + for (final InfoItem item : infoListAdapter.getItemsList()) { + if (item instanceof StreamInfoItem) { + streams.add((StreamInfoItem) item); + } + } + PlaylistAppendDialog.fromStreamInfoItems(streams).show(manager, tag); + } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 1b24a5dce..641b26299 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -84,6 +84,7 @@ public class ChannelFragment extends BaseListInfoFragment { private LinearLayout headerBackgroundButton; private MenuItem menuRssButton; + private MenuItem playlistAppendButton; public static ChannelFragment getInstance(int serviceId, String url, String name) { ChannelFragment instance = new ChannelFragment(); @@ -194,17 +195,20 @@ public class ChannelFragment extends BaseListInfoFragment { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); ActionBar supportActionBar = activity.getSupportActionBar(); - if(useAsFrontPage) { + if(useAsFrontPage && supportActionBar != null) { supportActionBar.setDisplayHomeAsUpEnabled(false); } else { inflater.inflate(R.menu.menu_channel, menu); - if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + + "], inflater = [" + inflater + "]"); menuRssButton = menu.findItem(R.id.menu_item_rss); + playlistAppendButton = menu.findItem(R.id.menu_append_playlist); + if (currentInfo != null) { menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl())); + playlistAppendButton.setVisible(!currentInfo.getRelatedStreams().isEmpty()); } - } } @@ -225,10 +229,12 @@ public class ChannelFragment extends BaseListInfoFragment { case R.id.menu_item_openInBrowser: openUrlInBrowser(url); break; - case R.id.menu_item_share: { + case R.id.menu_item_share: shareUrl(name, url); break; - } + case R.id.menu_append_playlist: + appendToPlaylist(getFragmentManager(), TAG); + break; default: return super.onOptionsItemSelected(item); } @@ -428,6 +434,9 @@ public class ChannelFragment extends BaseListInfoFragment { } else headerSubscribersTextView.setVisibility(View.GONE); if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl())); + if (playlistAppendButton != null) playlistAppendButton + .setVisible(!currentInfo.getRelatedStreams().isEmpty()); + playlistCtrl.setVisibility(View.VISIBLE); if (!result.errors.isEmpty()) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 52eeb337c..15255618b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -54,6 +54,8 @@ public class PlaylistFragment extends BaseListInfoFragment { private View headerPopupButton; private View headerBackgroundButton; + private MenuItem playlistAppendButton; + public static PlaylistFragment getInstance(int serviceId, String url, String name) { PlaylistFragment instance = new PlaylistFragment(); instance.setInitialData(serviceId, url, name); @@ -141,9 +143,15 @@ public class PlaylistFragment extends BaseListInfoFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + + "], inflater = [" + inflater + "]"); super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_playlist, menu); + + playlistAppendButton = menu.findItem(R.id.menu_append_playlist); + if (currentInfo != null) { + playlistAppendButton.setVisible(!currentInfo.getRelatedStreams().isEmpty()); + } } /*////////////////////////////////////////////////////////////////////////// @@ -166,10 +174,12 @@ public class PlaylistFragment extends BaseListInfoFragment { case R.id.menu_item_openInBrowser: openUrlInBrowser(url); break; - case R.id.menu_item_share: { + case R.id.menu_item_share: shareUrl(name, url); break; - } + case R.id.menu_append_playlist: + appendToPlaylist(getFragmentManager(), TAG); + break; default: return super.onOptionsItemSelected(item); } @@ -215,6 +225,9 @@ public class PlaylistFragment extends BaseListInfoFragment { imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS); headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count)); + if (playlistAppendButton != null) playlistAppendButton + .setVisible(!currentInfo.getRelatedStreams().isEmpty()); + if (!result.getErrors().isEmpty()) { showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalItemListAdapter.java index af1a0f666..0e012aad7 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/LocalItemListAdapter.java @@ -58,7 +58,6 @@ public class LocalItemListAdapter extends RecyclerView.Adapter(); @@ -99,21 +98,16 @@ public class LocalItemListAdapter extends RecyclerView.Adapter= localItems.size()) return; - - localItems.remove(infoListPosition); - - notifyItemRemoved(infoListPosition + (header != null ? 1 : 0)); + localItems.remove(index); + notifyItemRemoved(index + (header != null ? 1 : 0)); } public boolean swapItems(int fromAdapterPosition, int toAdapterPosition) { - final int actualFrom = offsetWithoutHeader(fromAdapterPosition); - final int actualTo = offsetWithoutHeader(toAdapterPosition); + final int actualFrom = adapterOffsetWithoutHeader(fromAdapterPosition); + final int actualTo = adapterOffsetWithoutHeader(toAdapterPosition); if (actualFrom < 0 || actualTo < 0) return false; if (actualFrom >= localItems.size() || actualTo >= localItems.size()) return false; @@ -150,7 +144,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter, Void> { + private static final long SAVE_DEBOUNCE_MILLIS = 1000; + private View headerRootLayout; private TextView headerTitleView; private TextView headerStreamCount; @@ -66,6 +71,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment debouncedSaveSignal; + private Disposable debouncedSaver; + public static LocalPlaylistFragment getInstance(long playlistId, String name) { LocalPlaylistFragment instance = new LocalPlaylistFragment(); instance.setInitialData(playlistId, name); @@ -89,11 +97,22 @@ public class LocalPlaylistFragment extends BaseLocalListFragment successToast.show()); } + private void saveDebounced() { + debouncedSaveSignal.onNext(System.currentTimeMillis()); + } + + private Disposable getDebouncedSaver() { + return debouncedSaveSignal + .debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .doOnDispose(this::saveJoin) + .subscribe(ignored -> saveJoin()); + } + private void saveJoin() { final List items = itemListAdapter.getItemsList(); List streamIds = new ArrayList<>(items.size()); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java index 302a37002..d4b6bd964 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistAppendDialog.java @@ -99,8 +99,8 @@ public final class PlaylistAppendDialog extends PlaylistDialog { return; final long playlistId = ((PlaylistMetadataEntry) selectedItem).uid; - final Toast successToast = - Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT); + final Toast successToast = Toast.makeText(getContext(), + R.string.playlist_add_stream_success, Toast.LENGTH_SHORT); playlistManager.appendToPlaylist(playlistId, getStreams()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java index 791e90fa2..670ae9819 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/PlaylistCreationDialog.java @@ -48,7 +48,7 @@ public final class PlaylistCreationDialog extends PlaylistDialog { final LocalPlaylistManager playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext())); final Toast successToast = Toast.makeText(getActivity(), - "Playlist successfully created", + R.string.playlist_creation_success, Toast.LENGTH_SHORT); playlistManager.createPlaylist(name, getStreams()) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/BookmarkFragment.java index 0bd0fa00f..ce80bcf0d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/BookmarkFragment.java @@ -174,8 +174,8 @@ public class BookmarkFragment extends BaseStateFragment { - final Toast deleteSuccessful = - Toast.makeText(getContext(), "Deleted", Toast.LENGTH_SHORT); + final Toast deleteSuccessful = Toast.makeText(getContext(), + R.string.playlist_delete_success, Toast.LENGTH_SHORT); disposables.add(localPlaylistManager.deletePlaylist(item.uid) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> deleteSuccessful.show())); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/StatisticsPlaylistFragment.java index cb2d671cc..1a872f382 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/local/bookmark/StatisticsPlaylistFragment.java @@ -275,7 +275,7 @@ public abstract class StatisticsPlaylistFragment if (super.onError(exception)) return true; onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, - "none", "History", R.string.general_error); + "none", "History Statistics", R.string.general_error); return true; } diff --git a/app/src/main/res/layout/dialog_playlists.xml b/app/src/main/res/layout/dialog_playlists.xml index 5abe91a8e..8c639fff6 100644 --- a/app/src/main/res/layout/dialog_playlists.xml +++ b/app/src/main/res/layout/dialog_playlists.xml @@ -28,7 +28,7 @@ android:layout_height="50dp" android:layout_toRightOf="@+id/newPlaylistIcon" android:gravity="left|center" - android:text="Create New Playlist" + android:text="@string/create_playlist" android:textAppearance="?android:attr/textAppearanceLarge" android:textSize="15sp" android:textStyle="bold" diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index cf555ffa5..3861a380d 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -159,112 +159,175 @@ android:baselineAligned="false" android:orientation="horizontal"> + + + + + + + + + + - + android:layout_height="match_parent" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:paddingLeft="6dp" + android:paddingRight="6dp"> + - + - + - + - + - + + + + + - - - - - - - - - - - + + diff --git a/app/src/main/res/menu/menu_playlist.xml b/app/src/main/res/menu/menu_playlist.xml index f125c3fc7..a12fb2f49 100644 --- a/app/src/main/res/menu/menu_playlist.xml +++ b/app/src/main/res/menu/menu_playlist.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + + \ 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 c3604f953..1e6d3e641 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -384,5 +384,12 @@ Rename Playlist Name Add To Playlist + Set as Playlist Thumbnail + Do you want to delete this playlist? + Playlist successfully created + Added to playlist + Playlist thumbnail changed + Playlist renamed + Playlist deleted