mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-17 15:47:12 +00:00
Larger channel cards in search results
- Thumbnail larger (100dp) than the usual (92dp) throughout the app - Description lint count is 8 (normally 3)
This commit is contained in:
@@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelCardInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
|
||||
@@ -73,6 +74,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
private static final int MINI_CHANNEL_HOLDER_TYPE = 0x200;
|
||||
private static final int CHANNEL_HOLDER_TYPE = 0x201;
|
||||
private static final int GRID_CHANNEL_HOLDER_TYPE = 0x202;
|
||||
private static final int CARD_CHANNEL_HOLDER_TYPE = 0x203;
|
||||
private static final int MINI_PLAYLIST_HOLDER_TYPE = 0x300;
|
||||
private static final int PLAYLIST_HOLDER_TYPE = 0x301;
|
||||
private static final int GRID_PLAYLIST_HOLDER_TYPE = 0x302;
|
||||
@@ -249,7 +251,9 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
return STREAM_HOLDER_TYPE;
|
||||
}
|
||||
case CHANNEL:
|
||||
if (itemMode == ItemViewMode.GRID) {
|
||||
if (itemMode == ItemViewMode.CARD) {
|
||||
return CARD_CHANNEL_HOLDER_TYPE;
|
||||
} else if (itemMode == ItemViewMode.GRID) {
|
||||
return GRID_CHANNEL_HOLDER_TYPE;
|
||||
} else if (useMiniVariant) {
|
||||
return MINI_CHANNEL_HOLDER_TYPE;
|
||||
@@ -304,6 +308,8 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
return new ChannelMiniInfoItemHolder(infoItemBuilder, parent);
|
||||
case CHANNEL_HOLDER_TYPE:
|
||||
return new ChannelInfoItemHolder(infoItemBuilder, parent);
|
||||
case CARD_CHANNEL_HOLDER_TYPE:
|
||||
return new ChannelCardInfoItemHolder(infoItemBuilder, parent);
|
||||
case GRID_CHANNEL_HOLDER_TYPE:
|
||||
return new ChannelGridInfoItemHolder(infoItemBuilder, parent);
|
||||
case MINI_PLAYLIST_HOLDER_TYPE:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
||||
public class ChannelCardInfoItemHolder extends ChannelMiniInfoItemHolder {
|
||||
public ChannelCardInfoItemHolder(final InfoItemBuilder infoItemBuilder,
|
||||
final ViewGroup parent) {
|
||||
super(infoItemBuilder, R.layout.list_channel_card_item, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDescriptionMaxLineCount(@Nullable final String content) {
|
||||
// Based on `list_channel_card_item` left side content (thumbnail 100dp
|
||||
// + additional details), Right side description can grow up to 8 lines.
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||
|
||||
itemTitleView.setText(item.getName());
|
||||
itemTitleView.setSelected(true);
|
||||
|
||||
final String detailLine = getDetailLine(item);
|
||||
if (detailLine == null) {
|
||||
@@ -77,11 +78,24 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
} else {
|
||||
itemChannelDescriptionView.setVisibility(View.VISIBLE);
|
||||
itemChannelDescriptionView.setText(item.getDescription());
|
||||
itemChannelDescriptionView.setMaxLines(detailLine == null ? 3 : 2);
|
||||
// setMaxLines utilize the line space for description if the additional details
|
||||
// (sub / video count) are not present.
|
||||
// Case1: 2 lines of description + 1 line additional details
|
||||
// Case2: 3 lines of description (additionalDetails is GONE)
|
||||
itemChannelDescriptionView.setMaxLines(getDescriptionMaxLineCount(detailLine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns max number of allowed lines for the description field.
|
||||
* @param content additional detail content (video / sub count)
|
||||
* @return max line count
|
||||
*/
|
||||
protected int getDescriptionMaxLineCount(@Nullable final String content) {
|
||||
return content == null ? 3 : 2;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getDetailLine(final ChannelInfoItem item) {
|
||||
if (item.getStreamCount() >= 0 && item.getSubscriberCount() >= 0) {
|
||||
|
||||
@@ -60,7 +60,6 @@ import org.schabi.newpipe.util.NavigationHelper
|
||||
import org.schabi.newpipe.util.OnClickGesture
|
||||
import org.schabi.newpipe.util.ServiceHelper
|
||||
import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels
|
||||
import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
@@ -245,7 +244,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
super.initViews(rootView, savedInstanceState)
|
||||
_binding = FragmentSubscriptionBinding.bind(rootView)
|
||||
|
||||
groupAdapter.spanCount = if (shouldUseGridLayout(context)) getGridSpanCountChannels(context) else 1
|
||||
groupAdapter.spanCount = if (SubscriptionViewModel.shouldUseGridForSubscription(requireContext())) getGridSpanCountChannels(context) else 1
|
||||
binding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply {
|
||||
spanSizeLookup = groupAdapter.spanSizeLookup
|
||||
}
|
||||
@@ -380,15 +379,15 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
override fun handleResult(result: SubscriptionState) {
|
||||
super.handleResult(result)
|
||||
|
||||
val shouldUseGridLayout = shouldUseGridLayout(context)
|
||||
when (result) {
|
||||
is SubscriptionState.LoadedState -> {
|
||||
result.subscriptions.forEach {
|
||||
if (it is ChannelItem) {
|
||||
it.gesturesListener = listenerChannelItem
|
||||
it.itemVersion = when {
|
||||
shouldUseGridLayout -> ChannelItem.ItemVersion.GRID
|
||||
else -> ChannelItem.ItemVersion.MINI
|
||||
it.itemVersion = if (SubscriptionViewModel.shouldUseGridForSubscription(requireContext())) {
|
||||
ChannelItem.ItemVersion.GRID
|
||||
} else {
|
||||
ChannelItem.ItemVersion.MINI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -8,12 +9,13 @@ import com.xwray.groupie.Group
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.processors.BehaviorProcessor
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.info_list.ItemViewMode
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
import org.schabi.newpipe.local.subscription.item.ChannelItem
|
||||
import org.schabi.newpipe.local.subscription.item.FeedGroupCardGridItem
|
||||
import org.schabi.newpipe.local.subscription.item.FeedGroupCardItem
|
||||
import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import org.schabi.newpipe.util.ThemeHelper.getItemViewMode
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SubscriptionViewModel(application: Application) : AndroidViewModel(application) {
|
||||
@@ -22,7 +24,7 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
|
||||
|
||||
// true -> list view, false -> grid view
|
||||
private val listViewMode = BehaviorProcessor.createDefault(
|
||||
!ThemeHelper.shouldUseGridLayout(application)
|
||||
!shouldUseGridForSubscription(application)
|
||||
)
|
||||
private val listViewModeFlowable = listViewMode.distinctUntilChanged()
|
||||
|
||||
@@ -77,4 +79,26 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
|
||||
data class LoadedState(val subscriptions: List<Group>) : SubscriptionState()
|
||||
data class ErrorState(val error: Throwable? = null) : SubscriptionState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Returns whether to use GridLayout mode for Subscription Fragment.
|
||||
*
|
||||
* ### Current mapping:
|
||||
*
|
||||
* | ItemViewMode | ItemVersion | Span count |
|
||||
* |---|---|---|
|
||||
* | AUTO | MINI | 1 |
|
||||
* | LIST | MINI | 1 |
|
||||
* | CARD | GRID | > 1 (ThemeHelper defined) |
|
||||
* | GRID | GRID | > 1 (ThemeHelper defined) |
|
||||
*
|
||||
* @see [SubscriptionViewModel.shouldUseGridForSubscription] to modify Layout Manager
|
||||
*/
|
||||
fun shouldUseGridForSubscription(context: Context): Boolean {
|
||||
val itemViewMode = getItemViewMode(context)
|
||||
return itemViewMode == ItemViewMode.GRID || itemViewMode == ItemViewMode.CARD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
app/src/main/res/layout/list_channel_card_item.xml
Normal file
74
app/src/main/res/layout/list_channel_card_item.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/channel_item_grid_padding">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
android:layout_width="@dimen/channel_item_card_thumbnail_image_size"
|
||||
android:layout_height="@dimen/channel_item_card_thumbnail_image_size"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
|
||||
android:src="@drawable/placeholder_person"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearance="@style/CircularImageView"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitleView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_mid"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"
|
||||
android:textStyle="normal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/itemThumbnailView"
|
||||
app:layout_constraintTop_toTopOf="@id/itemThumbnailView"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="@sample/channels.json/data/name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemChannelDescriptionView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/itemTitleView"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="8"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/itemTitleView"
|
||||
app:layout_constraintTop_toBottomOf="@id/itemTitleView"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="@sample/channels.json/data/description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemAdditionalDetails"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/spacing_micro"
|
||||
android:gravity="center"
|
||||
android:lines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
android:textStyle="normal"
|
||||
app:layout_constraintEnd_toEndOf="@id/itemThumbnailView"
|
||||
app:layout_constraintStart_toStartOf="@id/itemThumbnailView"
|
||||
app:layout_constraintTop_toBottomOf="@id/itemThumbnailView"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="@sample/channels.json/data/additional" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -4,6 +4,7 @@
|
||||
<dimen name="margin_normal">16dp</dimen>
|
||||
<dimen name="margin_small">8dp</dimen>
|
||||
<dimen name="margin_large">32dp</dimen>
|
||||
<dimen name="spacing_mid">12dp</dimen>
|
||||
<dimen name="spacing_normal">8dp</dimen>
|
||||
<dimen name="spacing_micro">4dp</dimen>
|
||||
<dimen name="spacing_nano">2dp</dimen>
|
||||
@@ -38,6 +39,7 @@
|
||||
<dimen name="video_item_grid_thumbnail_image_width">164dp</dimen>
|
||||
<dimen name="video_item_grid_thumbnail_image_height">92dp</dimen>
|
||||
|
||||
<dimen name="channel_item_card_thumbnail_image_size">100dp</dimen>
|
||||
<dimen name="channel_item_grid_thumbnail_image_size">92dp</dimen>
|
||||
<dimen name="channel_item_grid_min_width">128dp</dimen>
|
||||
<!-- Calculated: 2*video_item_search_padding + video_item_search_thumbnail_image_height -->
|
||||
|
||||
Reference in New Issue
Block a user