Merge pull request #9728 from mahendranv/channel_card

Larger channel cards in search results
pull/9746/head
Stypox 2023-02-26 13:43:09 +01:00 zatwierdzone przez GitHub
commit 2ee4c6e289
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
8 zmienionych plików z 170 dodań i 10 usunięć

Wyświetl plik

@ -0,0 +1,19 @@
{
"data": [
{
"name": "BBC",
"additional": "12K subscribers•233 videos",
"description": "The BBC is the worlds leading public service broadcaster. Were impartial and independent, and every day we create distinctive, world-class programmes and content which inform, educate and entertain millions of people in the UK and around the world. SUBSCRIBE to our YouTube channel to get the best of BBC entertainment and comedy programmes, stories from science and nature documentaries, and much more! https://bit.ly/2IXqEIn Get ALL your fresh TV, and sofa-hugging box sets on iPlayer https://bbc.in/2J18jYJ"
},
{
"name": "Linus Tech Tips",
"additional": "1M subscribers•233 videos",
"description": "Looking for a Tech YouTuber?\n\nLinus Tech Tips is a passionate team of \"professionally curious\" experts in consumer technology and video production which aims to inform and educate people of all ages through our entertaining videos. We create product reviews, step-by-step computer build guides, and a variety of other tech-focused content.\n\nSchedule:\nNew videos every Saturday to Thursday @ 10:00am Pacific\nLive WAN Show podcasts every Friday @ ~5:00pm Pacific"
},
{
"name": "Marques Brownlee",
"additional": "13 subscribers•12K videos",
"description": "MKBHD: Quality Tech Videos | YouTuber | Geek | Consumer Electronics | Tech Head | Internet Personality!\n\nbusiness@MKBHD.com\n\nNYC"
}
]
}

Wyświetl plik

@ -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:

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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
}
}
}

Wyświetl plik

@ -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
}
}
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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 -->