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 1212cf4ad..0b0acff86 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 @@ -26,6 +26,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.info_list.dialog.InfoItemDialog; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -91,11 +92,7 @@ public abstract class BaseListFragment extends BaseStateFragment if (updateFlags != 0) { if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) { - final boolean useGrid = isGridLayout(); - itemsList.setLayoutManager(useGrid - ? getGridLayoutManager() : getListLayoutManager()); - infoListAdapter.setUseGridVariant(useGrid); - infoListAdapter.notifyDataSetChanged(); + refreshItemViewMode(); } updateFlags = 0; } @@ -221,15 +218,23 @@ public abstract class BaseListFragment extends BaseStateFragment return lm; } + /** + * Updates the item view mode based on user preference. + */ + private void refreshItemViewMode() { + final ItemViewMode itemViewMode = getItemViewMode(); + itemsList.setLayoutManager((itemViewMode == ItemViewMode.GRID) + ? getGridLayoutManager() : getListLayoutManager()); + infoListAdapter.setItemViewMode(itemViewMode); + infoListAdapter.notifyDataSetChanged(); + } + @Override protected void initViews(final View rootView, final Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); - final boolean useGrid = isGridLayout(); itemsList = rootView.findViewById(R.id.items_list); - itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); - - infoListAdapter.setUseGridVariant(useGrid); + refreshItemViewMode(); final Supplier listHeaderSupplier = getListHeaderSupplier(); if (listHeaderSupplier != null) { @@ -474,7 +479,11 @@ public abstract class BaseListFragment extends BaseStateFragment } } - protected boolean isGridLayout() { - return ThemeHelper.shouldUseGridLayout(activity); + /** + * Returns preferred item view mode. + * @return ItemViewMode + */ + protected ItemViewMode getItemViewMode() { + return ThemeHelper.getItemViewMode(requireContext()); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java index 3b092cc28..5a5f84968 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java @@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.util.ExtractorHelper; @@ -106,7 +107,7 @@ public class CommentsFragment extends BaseListInfoFragment headerSupplier = null; public InfoListAdapter(final Context context) { @@ -114,8 +119,8 @@ public class InfoListAdapter extends RecyclerView.Adapter data) { @@ -234,14 +239,33 @@ public class InfoListAdapter extends RecyclerView.Adapter extends BaseStateFragment super.onResume(); if (updateFlags != 0) { if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) { - final boolean useGrid = shouldUseGridLayout(requireContext()); - itemsList.setLayoutManager( - useGrid ? getGridLayoutManager() : getListLayoutManager()); - itemListAdapter.setUseGridVariant(useGrid); - itemListAdapter.notifyDataSetChanged(); + refreshItemViewMode(); } updateFlags = 0; } } + /** + * Updates the item view mode based on user preference. + */ + private void refreshItemViewMode() { + final ItemViewMode itemViewMode = getItemViewMode(requireContext()); + itemsList.setLayoutManager((itemViewMode == ItemViewMode.GRID) + ? getGridLayoutManager() : getListLayoutManager()); + itemListAdapter.setItemViewMode(itemViewMode); + itemListAdapter.notifyDataSetChanged(); + } + /*////////////////////////////////////////////////////////////////////////// // Lifecycle - View //////////////////////////////////////////////////////////////////////////*/ @@ -120,11 +128,9 @@ public abstract class BaseLocalListFragment extends BaseStateFragment itemListAdapter = new LocalItemListAdapter(activity); - final boolean useGrid = shouldUseGridLayout(requireContext()); itemsList = rootView.findViewById(R.id.items_list); - itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); + refreshItemViewMode(); - itemListAdapter.setUseGridVariant(useGrid); headerRootBinding = getListHeader(); if (headerRootBinding != null) { itemListAdapter.setHeader(headerRootBinding.getRoot()); diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index 05e2fdac0..b9409cb9d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -12,14 +12,19 @@ import androidx.recyclerview.widget.RecyclerView; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.model.StreamStateEntity; +import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.local.holder.LocalItemHolder; +import org.schabi.newpipe.local.holder.LocalPlaylistCardItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder; +import org.schabi.newpipe.local.holder.LocalPlaylistStreamCardItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; +import org.schabi.newpipe.local.holder.LocalStatisticStreamCardItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder; +import org.schabi.newpipe.local.holder.RemotePlaylistCardItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; import org.schabi.newpipe.util.FallbackViewHolder; @@ -61,11 +66,17 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; @@ -73,9 +84,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter() { @SuppressLint("StringFormatMatches") private fun handleLoadedState(loadedState: FeedState.LoadedState) { - - val itemVersion = if (shouldUseGridLayout(context)) { - StreamItem.ItemVersion.GRID - } else { - StreamItem.ItemVersion.NORMAL + val itemVersion = when (getItemViewMode(requireContext())) { + ItemViewMode.GRID -> StreamItem.ItemVersion.GRID + ItemViewMode.CARD -> StreamItem.ItemVersion.CARD + else -> StreamItem.ItemVersion.NORMAL } loadedState.items.forEach { it.itemVersion = itemVersion } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt index 96d395aa5..d795dcb08 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt @@ -42,12 +42,13 @@ data class StreamItem( override fun getId(): Long = stream.uid - enum class ItemVersion { NORMAL, MINI, GRID } + enum class ItemVersion { NORMAL, MINI, GRID, CARD } override fun getLayout(): Int = when (itemVersion) { ItemVersion.NORMAL -> R.layout.list_stream_item ItemVersion.MINI -> R.layout.list_stream_mini_item ItemVersion.GRID -> R.layout.list_stream_grid_item + ItemVersion.CARD -> R.layout.list_stream_card_item } override fun initializeViewBinding(view: View) = ListStreamItemBinding.bind(view) diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java new file mode 100644 index 000000000..33418ec98 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.local.holder; + +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.local.LocalItemBuilder; + +/** + * Playlist card layout. + */ +public class LocalPlaylistCardItemHolder extends LocalPlaylistItemHolder { + + public LocalPlaylistCardItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + super(infoItemBuilder, R.layout.list_playlist_card_item, parent); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java new file mode 100644 index 000000000..7f81a527f --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.local.holder; + +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.local.LocalItemBuilder; + +/** + * Local playlist stream UI. This also includes a handle to rearrange the videos. + */ +public class LocalPlaylistStreamCardItemHolder extends LocalPlaylistStreamItemHolder { + + public LocalPlaylistStreamCardItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + super(infoItemBuilder, R.layout.list_stream_playlist_card_item, parent); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java new file mode 100644 index 000000000..4e03d5fb1 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java @@ -0,0 +1,13 @@ +package org.schabi.newpipe.local.holder; + +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.local.LocalItemBuilder; + +public class LocalStatisticStreamCardItemHolder extends LocalStatisticStreamItemHolder { + public LocalStatisticStreamCardItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + super(infoItemBuilder, R.layout.list_stream_card_item, parent); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java new file mode 100644 index 000000000..74a67c3db --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.local.holder; + +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.local.LocalItemBuilder; + +/** + * Playlist card UI for list item. + */ +public class RemotePlaylistCardItemHolder extends RemotePlaylistItemHolder { + + public RemotePlaylistCardItemHolder(final LocalItemBuilder infoItemBuilder, + final ViewGroup parent) { + super(infoItemBuilder, R.layout.list_playlist_card_item, parent); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java index ea22e9368..ab74e0305 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java @@ -41,6 +41,7 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.info_list.ItemViewMode; public final class ThemeHelper { private ThemeHelper() { @@ -332,7 +333,6 @@ public final class ThemeHelper { } } - /** * Returns whether the grid layout or the list layout should be used. If the user set "auto" * mode in settings, decides based on screen orientation (landscape) and size. @@ -341,19 +341,8 @@ public final class ThemeHelper { * @return true:use grid layout, false:use list layout */ public static boolean shouldUseGridLayout(final Context context) { - final String listMode = PreferenceManager.getDefaultSharedPreferences(context) - .getString(context.getString(R.string.list_view_mode_key), - context.getString(R.string.list_view_mode_value)); - - if (listMode.equals(context.getString(R.string.list_view_mode_list_key))) { - return false; - } else if (listMode.equals(context.getString(R.string.list_view_mode_grid_key))) { - return true; - } else /* listMode.equals("auto") */ { - final Configuration configuration = context.getResources().getConfiguration(); - return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE - && configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); - } + final ItemViewMode mode = getItemViewMode(context); + return mode == ItemViewMode.GRID; } /** @@ -367,6 +356,36 @@ public final class ThemeHelper { context.getResources().getDimensionPixelSize(R.dimen.channel_item_grid_min_width)); } + /** + * Returns item view mode. + * @param context to read preference and parse string + * @return Returns one of ItemViewMode + */ + public static ItemViewMode getItemViewMode(final Context context) { + final String listMode = PreferenceManager.getDefaultSharedPreferences(context) + .getString(context.getString(R.string.list_view_mode_key), + context.getString(R.string.list_view_mode_value)); + final ItemViewMode result; + if (listMode.equals(context.getString(R.string.list_view_mode_list_key))) { + result = ItemViewMode.LIST; + } else if (listMode.equals(context.getString(R.string.list_view_mode_grid_key))) { + result = ItemViewMode.GRID; + } else if (listMode.equals(context.getString(R.string.list_view_mode_card_key))) { + result = ItemViewMode.CARD; + } else { + // Auto mode - evaluate whether to use Grid based on screen real estate. + final Configuration configuration = context.getResources().getConfiguration(); + final boolean useGrid = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + && configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); + if (useGrid) { + result = ItemViewMode.GRID; + } else { + result = ItemViewMode.LIST; + } + } + return result; + } + /** * Calculates the number of grid stream info items that can fit horizontally on the screen. The * width of a grid stream info item is obtained from the thumbnail width plus the right and left diff --git a/app/src/main/res/layout/list_playlist_card_item.xml b/app/src/main/res/layout/list_playlist_card_item.xml new file mode 100644 index 000000000..c7dd4f17c --- /dev/null +++ b/app/src/main/res/layout/list_playlist_card_item.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_stream_card_item.xml b/app/src/main/res/layout/list_stream_card_item.xml new file mode 100644 index 000000000..968dca082 --- /dev/null +++ b/app/src/main/res/layout/list_stream_card_item.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_stream_playlist_card_item.xml b/app/src/main/res/layout/list_stream_playlist_card_item.xml new file mode 100644 index 000000000..9cc6b326c --- /dev/null +++ b/app/src/main/res/layout/list_stream_playlist_card_item.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml index 63fc81644..817cd8f85 100644 --- a/app/src/main/res/values-w820dp/dimens.xml +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -3,4 +3,7 @@ (such as screen margins) for screens with more than 820dp of available width. This would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> 64dp + + 280dp + 160dp diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 5693ec7c4..679dc05eb 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,5 +1,13 @@ + + 16dp + 8dp + 32dp + 8dp + 4dp + 2dp + 120dp 16dp diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 17ed547a0..1a711ad17 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1263,16 +1263,19 @@ auto list grid + card @string/list_view_mode_auto_key @string/list_view_mode_list_key @string/list_view_mode_grid_key + @string/list_view_mode_card_key @string/auto @string/list @string/grid + @string/card tablet_mode diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2deb14d8d..5a4ce92f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -550,6 +550,7 @@ List view mode List Grid + Card Auto Seekbar thumbnail preview