Fix grid/list toggle implementation of feed

This commit is contained in:
Stypox 2022-10-26 23:20:32 +02:00
parent 8b9db369f6
commit 0e169951f7
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
19 changed files with 354 additions and 383 deletions

View File

@ -20,10 +20,8 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.xwray.groupie.Group import com.xwray.groupie.Group
import com.xwray.groupie.GroupAdapter import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.Section import com.xwray.groupie.Section
import com.xwray.groupie.viewbinding.GroupieViewHolder import com.xwray.groupie.viewbinding.GroupieViewHolder
import icepick.State import icepick.State
@ -44,13 +42,13 @@ import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialog import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialog
import org.schabi.newpipe.local.subscription.item.ChannelItem import org.schabi.newpipe.local.subscription.item.ChannelItem
import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem
import org.schabi.newpipe.local.subscription.item.FeedGroupAddItem import org.schabi.newpipe.local.subscription.item.FeedGroupAddNewGridItem
import org.schabi.newpipe.local.subscription.item.FeedGroupAddVerticalItem import org.schabi.newpipe.local.subscription.item.FeedGroupAddNewItem
import org.schabi.newpipe.local.subscription.item.FeedGroupCardGridItem
import org.schabi.newpipe.local.subscription.item.FeedGroupCardItem import org.schabi.newpipe.local.subscription.item.FeedGroupCardItem
import org.schabi.newpipe.local.subscription.item.FeedGroupCardVerticalItem
import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
import org.schabi.newpipe.local.subscription.item.HeaderWithMenuItem import org.schabi.newpipe.local.subscription.item.GroupsHeader
import org.schabi.newpipe.local.subscription.item.HeaderWithMenuItem.Companion.PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM import org.schabi.newpipe.local.subscription.item.Header
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE
@ -77,11 +75,10 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
private val disposables: CompositeDisposable = CompositeDisposable() private val disposables: CompositeDisposable = CompositeDisposable()
private val groupAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>() private val groupAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
private val feedGroupsSection = Section() private lateinit var carouselAdapter: GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>
private var feedGroupsCarousel: FeedGroupCarouselItem? = null private lateinit var feedGroupsCarousel: FeedGroupCarouselItem
private lateinit var feedGroupsSortMenuItem: HeaderWithMenuItem private lateinit var feedGroupsSortMenuItem: GroupsHeader
private val subscriptionsSection = Section() private val subscriptionsSection = Section()
private var defaultListView: Boolean = true
private val requestExportLauncher = private val requestExportLauncher =
registerForActivityResult(StartActivityForResult(), this::requestExportResult) registerForActivityResult(StartActivityForResult(), this::requestExportResult)
@ -94,11 +91,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
@State @State
@JvmField @JvmField
var feedGroupsListState: Parcelable? = null var feedGroupsCarouselState: Parcelable? = null
@State
@JvmField
var feedGroupsListVerticalState: Parcelable? = null
init { init {
setHasOptionsMenu(true) setHasOptionsMenu(true)
@ -108,11 +101,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
// Fragment LifeCycle // Fragment LifeCycle
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupInitialLayout()
}
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
subscriptionManager = SubscriptionManager(requireContext()) subscriptionManager = SubscriptionManager(requireContext())
@ -125,8 +113,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
itemsListState = binding.itemsList.layoutManager?.onSaveInstanceState() itemsListState = binding.itemsList.layoutManager?.onSaveInstanceState()
feedGroupsListState = feedGroupsCarousel?.onSaveInstanceState() feedGroupsCarouselState = feedGroupsCarousel.onSaveInstanceState()
feedGroupsListVerticalState = feedGroupsCarousel?.onSaveInstanceState()
} }
override fun onDestroy() { override fun onDestroy() {
@ -193,7 +180,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
menuItem: MenuItem, menuItem: MenuItem,
onClick: Runnable onClick: Runnable
): MenuItem { ): MenuItem {
menuItem.setOnMenuItemClickListener { _ -> menuItem.setOnMenuItemClickListener {
onClick.run() onClick.run()
true true
} }
@ -254,105 +241,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
// Fragment Views // Fragment Views
// //////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////
private fun setupInitialLayout() {
defaultListView = true
Section().apply {
val carouselAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
carouselAdapter.add(FeedGroupCardItem(-1, getString(R.string.all), FeedGroupIcon.RSS))
carouselAdapter.add(feedGroupsSection)
carouselAdapter.add(FeedGroupAddItem())
carouselAdapter.setOnItemClickListener { item, _ ->
listenerFeedGroups.selected(item)
}
carouselAdapter.setOnItemLongClickListener { item, _ ->
if (item is FeedGroupCardItem) {
if (item.groupId == FeedGroupEntity.GROUP_ALL_ID) {
return@setOnItemLongClickListener false
}
}
listenerFeedGroups.held(item)
return@setOnItemLongClickListener true
}
feedGroupsCarousel = FeedGroupCarouselItem(requireContext(), carouselAdapter, RecyclerView.HORIZONTAL, true)
feedGroupsSortMenuItem = HeaderWithMenuItem(
getString(R.string.feed_groups_header_title),
R.drawable.ic_list,
R.drawable.ic_sort,
listViewOnClickListener = ::changeVerticalLayout,
menuItemOnClickListener = ::openReorderDialog
)
add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel)))
groupAdapter.clear()
groupAdapter.add(this)
}
subscriptionsSection.setPlaceholder(EmptyPlaceholderItem())
subscriptionsSection.setHideWhenEmpty(true)
groupAdapter.add(
Section(
HeaderWithMenuItem(
getString(R.string.tab_subscriptions)
),
listOf(subscriptionsSection)
)
)
view?.let { initViews(it, savedInstanceState = Bundle()) }
}
private fun changeVerticalLayout() {
defaultListView = false
Section().apply {
val carouselAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
carouselAdapter.add(FeedGroupCardVerticalItem(-1, getString(R.string.all), FeedGroupIcon.RSS))
carouselAdapter.add(feedGroupsSection)
carouselAdapter.add(FeedGroupAddVerticalItem())
carouselAdapter.setOnItemClickListener { item, _ ->
listenerFeedVerticalGroups.selected(item)
}
carouselAdapter.setOnItemLongClickListener { item, _ ->
if (item is FeedGroupCardVerticalItem) {
if (item.groupId == FeedGroupEntity.GROUP_ALL_ID) {
return@setOnItemLongClickListener false
}
}
listenerFeedVerticalGroups.held(item)
return@setOnItemLongClickListener true
}
feedGroupsCarousel = FeedGroupCarouselItem(requireContext(), carouselAdapter, RecyclerView.VERTICAL, false)
feedGroupsSortMenuItem = HeaderWithMenuItem(
getString(R.string.feed_groups_header_title),
R.drawable.ic_apps,
R.drawable.ic_sort,
listViewOnClickListener = ::setupInitialLayout,
menuItemOnClickListener = ::openReorderDialog
)
add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel)))
groupAdapter.clear()
groupAdapter.add(this)
}
subscriptionsSection.setPlaceholder(EmptyPlaceholderItem())
subscriptionsSection.setHideWhenEmpty(true)
groupAdapter.add(
Section(
HeaderWithMenuItem(
getString(R.string.tab_subscriptions)
),
listOf(subscriptionsSection)
)
)
view?.let { initViews(it, savedInstanceState = Bundle()) }
}
override fun initViews(rootView: View, savedInstanceState: Bundle?) { override fun initViews(rootView: View, savedInstanceState: Bundle?) {
super.initViews(rootView, savedInstanceState) super.initViews(rootView, savedInstanceState)
_binding = FragmentSubscriptionBinding.bind(rootView) _binding = FragmentSubscriptionBinding.bind(rootView)
@ -363,10 +251,81 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
} }
binding.itemsList.adapter = groupAdapter binding.itemsList.adapter = groupAdapter
viewModel = ViewModelProvider(this).get(SubscriptionViewModel::class.java) viewModel = ViewModelProvider(this)[SubscriptionViewModel::class.java]
viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(this::handleResult) } viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(this::handleResult) }
viewModel.feedGroupsLiveData.observe(viewLifecycleOwner) { it?.let(this::handleFeedGroups) } viewModel.feedGroupsLiveData.observe(viewLifecycleOwner) { it?.let(this::handleFeedGroups) }
viewModel.feedGroupsVerticalLiveData.observe(viewLifecycleOwner) { it?.let(this::handleFeedGroupsVertical) }
setupInitialLayout()
}
private fun setupInitialLayout() {
Section().apply {
carouselAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
carouselAdapter.setOnItemClickListener { item, _ ->
when (item) {
is FeedGroupCardItem ->
NavigationHelper.openFeedFragment(fm, item.groupId, item.name)
is FeedGroupCardGridItem ->
NavigationHelper.openFeedFragment(fm, item.groupId, item.name)
is FeedGroupAddNewItem ->
FeedGroupDialog.newInstance().show(fm, null)
is FeedGroupAddNewGridItem ->
FeedGroupDialog.newInstance().show(fm, null)
}
}
carouselAdapter.setOnItemLongClickListener { item, _ ->
if ((
item is FeedGroupCardItem &&
item.groupId == FeedGroupEntity.GROUP_ALL_ID
) ||
(
item is FeedGroupCardGridItem &&
item.groupId == FeedGroupEntity.GROUP_ALL_ID
)
) {
return@setOnItemLongClickListener false
}
when (item) {
is FeedGroupCardItem ->
FeedGroupDialog.newInstance(item.groupId).show(fm, null)
is FeedGroupCardGridItem ->
FeedGroupDialog.newInstance(item.groupId).show(fm, null)
}
return@setOnItemLongClickListener true
}
feedGroupsCarousel = FeedGroupCarouselItem(
carouselAdapter = carouselAdapter,
listViewMode = viewModel.getListViewMode()
)
feedGroupsSortMenuItem = GroupsHeader(
title = getString(R.string.feed_groups_header_title),
onSortClicked = ::openReorderDialog,
onToggleListViewModeClicked = ::toggleListViewMode,
listViewMode = viewModel.getListViewMode(),
)
add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel)))
groupAdapter.clear()
groupAdapter.add(this)
}
subscriptionsSection.setPlaceholder(EmptyPlaceholderItem())
subscriptionsSection.setHideWhenEmpty(true)
groupAdapter.add(
Section(
Header(getString(R.string.tab_subscriptions)),
listOf(subscriptionsSection)
)
)
}
private fun toggleListViewMode() {
viewModel.setListViewMode(!viewModel.getListViewMode())
} }
private fun showLongTapDialog(selectedItem: ChannelInfoItem) { private fun showLongTapDialog(selectedItem: ChannelInfoItem) {
@ -410,36 +369,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun doInitialLoadLogic() = Unit override fun doInitialLoadLogic() = Unit
override fun startLoading(forceLoad: Boolean) = Unit override fun startLoading(forceLoad: Boolean) = Unit
private val listenerFeedGroups = object : OnClickGesture<Item<*>> {
override fun selected(selectedItem: Item<*>?) {
when (selectedItem) {
is FeedGroupCardItem -> NavigationHelper.openFeedFragment(fm, selectedItem.groupId, selectedItem.name)
is FeedGroupAddItem -> FeedGroupDialog.newInstance().show(fm, null)
}
}
override fun held(selectedItem: Item<*>?) {
when (selectedItem) {
is FeedGroupCardItem -> FeedGroupDialog.newInstance(selectedItem.groupId).show(fm, null)
}
}
}
private val listenerFeedVerticalGroups = object : OnClickGesture<Item<*>> {
override fun selected(selectedItem: Item<*>?) {
when (selectedItem) {
is FeedGroupCardVerticalItem -> NavigationHelper.openFeedFragment(fm, selectedItem.groupId, selectedItem.name)
is FeedGroupAddVerticalItem -> FeedGroupDialog.newInstance().show(fm, null)
}
}
override fun held(selectedItem: Item<*>?) {
when (selectedItem) {
is FeedGroupCardVerticalItem -> FeedGroupDialog.newInstance(selectedItem.groupId).show(fm, null)
}
}
}
private val listenerChannelItem = object : OnClickGesture<ChannelInfoItem> { private val listenerChannelItem = object : OnClickGesture<ChannelInfoItem> {
override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment( override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment(
fm, fm,
@ -482,30 +411,29 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
} }
private fun handleFeedGroups(groups: List<Group>) { private fun handleFeedGroups(groups: List<Group>) {
if (defaultListView) { val listViewMode = viewModel.getListViewMode()
feedGroupsSection.update(groups)
if (feedGroupsListState != null) { carouselAdapter.clear()
feedGroupsCarousel?.onRestoreInstanceState(feedGroupsListState) carouselAdapter.add(
feedGroupsListState = null if (listViewMode)
} FeedGroupCardItem(-1, getString(R.string.all), FeedGroupIcon.RSS)
else
FeedGroupCardGridItem(-1, getString(R.string.all), FeedGroupIcon.RSS)
)
carouselAdapter.addAll(groups)
carouselAdapter.add(if (listViewMode) FeedGroupAddNewItem() else FeedGroupAddNewGridItem())
feedGroupsSortMenuItem.showMenuItem = groups.size > 1 if (feedGroupsCarouselState != null) {
binding.itemsList.post { feedGroupsSortMenuItem.notifyChanged(PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM) } feedGroupsCarousel.onRestoreInstanceState(feedGroupsCarouselState)
feedGroupsCarouselState = null
} }
}
private fun handleFeedGroupsVertical(groups: List<Group>) { feedGroupsCarousel.listViewMode = listViewMode
if (!defaultListView) { feedGroupsSortMenuItem.showSortButton = groups.size > 1
feedGroupsSection.update(groups) feedGroupsSortMenuItem.listViewMode = listViewMode
binding.itemsList.post {
if (feedGroupsListVerticalState != null) { feedGroupsCarousel.notifyChanged(FeedGroupCarouselItem.PAYLOAD_UPDATE_LIST_VIEW_MODE)
feedGroupsCarousel?.onRestoreInstanceState(feedGroupsListVerticalState) feedGroupsSortMenuItem.notifyChanged(GroupsHeader.PAYLOAD_UPDATE_ICONS)
feedGroupsListVerticalState = null
}
feedGroupsSortMenuItem.showMenuItem = groups.size > 1
binding.itemsList.post { feedGroupsSortMenuItem.notifyChanged(PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM) }
} }
} }

View File

@ -1,15 +1,20 @@
package org.schabi.newpipe.local.subscription package org.schabi.newpipe.local.subscription
import android.app.Application import android.app.Application
import android.content.res.Configuration
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.preference.PreferenceManager
import com.xwray.groupie.Group import com.xwray.groupie.Group
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.processors.BehaviorProcessor
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.R
import org.schabi.newpipe.local.feed.FeedDatabaseManager import org.schabi.newpipe.local.feed.FeedDatabaseManager
import org.schabi.newpipe.local.subscription.item.ChannelItem 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.local.subscription.item.FeedGroupCardItem
import org.schabi.newpipe.local.subscription.item.FeedGroupCardVerticalItem
import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -17,31 +22,31 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(application) private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(application)
private var subscriptionManager = SubscriptionManager(application) private var subscriptionManager = SubscriptionManager(application)
// true -> list view, false -> grid view
private val listViewMode = BehaviorProcessor.createDefault(!isGridLayout(application))
private val listViewModeFlowable = listViewMode.distinctUntilChanged()
private val mutableStateLiveData = MutableLiveData<SubscriptionState>() private val mutableStateLiveData = MutableLiveData<SubscriptionState>()
private val mutableFeedGroupsLiveData = MutableLiveData<List<Group>>() private val mutableFeedGroupsLiveData = MutableLiveData<List<Group>>()
private val mutableFeedGroupsVerticalLiveData = MutableLiveData<List<Group>>()
val stateLiveData: LiveData<SubscriptionState> = mutableStateLiveData val stateLiveData: LiveData<SubscriptionState> = mutableStateLiveData
val feedGroupsLiveData: LiveData<List<Group>> = mutableFeedGroupsLiveData val feedGroupsLiveData: LiveData<List<Group>> = mutableFeedGroupsLiveData
val feedGroupsVerticalLiveData: LiveData<List<Group>> = mutableFeedGroupsVerticalLiveData
private var feedGroupItemsDisposable = feedDatabaseManager.groups() private var feedGroupItemsDisposable = Flowable
.combineLatest(
feedDatabaseManager.groups(),
listViewModeFlowable,
::Pair
)
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS) .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
.map { it.map(::FeedGroupCardItem) } .map { (feedGroups, listViewMode) ->
feedGroups.map(if (listViewMode) ::FeedGroupCardItem else ::FeedGroupCardGridItem)
}
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe( .subscribe(
{ mutableFeedGroupsLiveData.postValue(it) }, { mutableFeedGroupsLiveData.postValue(it) },
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) } { mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
) )
private var feedGroupVerticalItemsDisposable = feedDatabaseManager.groups()
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
.map { it.map(::FeedGroupCardVerticalItem) }
.subscribeOn(Schedulers.io())
.subscribe(
{ mutableFeedGroupsVerticalLiveData.postValue(it) },
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
)
private var stateItemsDisposable = subscriptionManager.subscriptions() private var stateItemsDisposable = subscriptionManager.subscriptions()
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS) .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
.map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } } .map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } }
@ -55,11 +60,38 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
super.onCleared() super.onCleared()
stateItemsDisposable.dispose() stateItemsDisposable.dispose()
feedGroupItemsDisposable.dispose() feedGroupItemsDisposable.dispose()
feedGroupVerticalItemsDisposable.dispose() }
fun setListViewMode(newListViewMode: Boolean) {
listViewMode.onNext(newListViewMode)
}
fun getListViewMode(): Boolean {
return listViewMode.value ?: true
} }
sealed class SubscriptionState { sealed class SubscriptionState {
data class LoadedState(val subscriptions: List<Group>) : SubscriptionState() data class LoadedState(val subscriptions: List<Group>) : SubscriptionState()
data class ErrorState(val error: Throwable? = null) : SubscriptionState() data class ErrorState(val error: Throwable? = null) : SubscriptionState()
} }
companion object {
private fun isGridLayout(application: Application): Boolean {
val listMode = PreferenceManager.getDefaultSharedPreferences(application)
.getString(
application.getString(R.string.list_view_mode_key),
application.getString(R.string.list_view_mode_value)
)
return if ("auto" == listMode) {
val configuration: Configuration = application.resources.configuration
(
configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE)
)
} else {
"grid" == listMode
}
}
}
} }

View File

@ -1,35 +0,0 @@
package org.schabi.newpipe.local.subscription.decoration
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.schabi.newpipe.R
class FeedGroupCarouselDecoration(context: Context) : RecyclerView.ItemDecoration() {
private val marginStartEnd: Int
private val marginTopBottom: Int
private val marginBetweenItems: Int
init {
with(context.resources) {
marginStartEnd = getDimensionPixelOffset(R.dimen.feed_group_carousel_start_end_margin)
marginTopBottom = getDimensionPixelOffset(R.dimen.feed_group_carousel_top_bottom_margin)
marginBetweenItems = getDimensionPixelOffset(R.dimen.feed_group_carousel_between_items_margin)
}
}
override fun getItemOffsets(outRect: Rect, child: View, parent: RecyclerView, state: RecyclerView.State) {
val childAdapterPosition = parent.getChildAdapterPosition(child)
val childAdapterCount = parent.adapter?.itemCount ?: 0
outRect.set(marginBetweenItems, marginTopBottom, 0, marginTopBottom)
if (childAdapterPosition >= 0) {
outRect.left = marginStartEnd
} else if (childAdapterPosition == childAdapterCount - 1) {
outRect.right = marginStartEnd
}
}
}

View File

@ -0,0 +1,12 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.FeedGroupAddNewGridItemBinding
class FeedGroupAddNewGridItem : BindableItem<FeedGroupAddNewGridItemBinding>() {
override fun getLayout(): Int = R.layout.feed_group_add_new_grid_item
override fun bind(viewBinding: FeedGroupAddNewGridItemBinding, position: Int) {}
override fun initializeViewBinding(view: View) = FeedGroupAddNewGridItemBinding.bind(view)
}

View File

@ -5,7 +5,7 @@ import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.FeedGroupAddNewItemBinding import org.schabi.newpipe.databinding.FeedGroupAddNewItemBinding
class FeedGroupAddItem : BindableItem<FeedGroupAddNewItemBinding>() { class FeedGroupAddNewItem : BindableItem<FeedGroupAddNewItemBinding>() {
override fun getLayout(): Int = R.layout.feed_group_add_new_item override fun getLayout(): Int = R.layout.feed_group_add_new_item
override fun bind(viewBinding: FeedGroupAddNewItemBinding, position: Int) {} override fun bind(viewBinding: FeedGroupAddNewItemBinding, position: Int) {}
override fun initializeViewBinding(view: View) = FeedGroupAddNewItemBinding.bind(view) override fun initializeViewBinding(view: View) = FeedGroupAddNewItemBinding.bind(view)

View File

@ -1,12 +0,0 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.FeedGroupAddNewVerticalItemBinding
class FeedGroupAddVerticalItem : BindableItem<FeedGroupAddNewVerticalItemBinding>() {
override fun getLayout(): Int = R.layout.feed_group_add_new_vertical_item
override fun bind(viewBinding: FeedGroupAddNewVerticalItemBinding, position: Int) {}
override fun initializeViewBinding(view: View) = FeedGroupAddNewVerticalItemBinding.bind(view)
}

View File

@ -4,14 +4,14 @@ import android.view.View
import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.databinding.FeedGroupCardVerticalItemBinding import org.schabi.newpipe.databinding.FeedGroupCardGridItemBinding
import org.schabi.newpipe.local.subscription.FeedGroupIcon import org.schabi.newpipe.local.subscription.FeedGroupIcon
data class FeedGroupCardVerticalItem( data class FeedGroupCardGridItem(
val groupId: Long = FeedGroupEntity.GROUP_ALL_ID, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
val name: String, val name: String,
val icon: FeedGroupIcon val icon: FeedGroupIcon,
) : BindableItem<FeedGroupCardVerticalItemBinding>() { ) : BindableItem<FeedGroupCardGridItemBinding>() {
constructor (feedGroupEntity: FeedGroupEntity) : this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon) constructor (feedGroupEntity: FeedGroupEntity) : this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon)
override fun getId(): Long { override fun getId(): Long {
@ -21,12 +21,12 @@ data class FeedGroupCardVerticalItem(
} }
} }
override fun getLayout(): Int = R.layout.feed_group_card_vertical_item override fun getLayout(): Int = R.layout.feed_group_card_grid_item
override fun bind(viewBinding: FeedGroupCardVerticalItemBinding, position: Int) { override fun bind(viewBinding: FeedGroupCardGridItemBinding, position: Int) {
viewBinding.title.text = name viewBinding.title.text = name
viewBinding.icon.setImageResource(icon.getDrawableRes()) viewBinding.icon.setImageResource(icon.getDrawableRes())
} }
override fun initializeViewBinding(view: View) = FeedGroupCardVerticalItemBinding.bind(view) override fun initializeViewBinding(view: View) = FeedGroupCardGridItemBinding.bind(view)
} }

View File

@ -10,7 +10,7 @@ import org.schabi.newpipe.local.subscription.FeedGroupIcon
data class FeedGroupCardItem( data class FeedGroupCardItem(
val groupId: Long = FeedGroupEntity.GROUP_ALL_ID, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
val name: String, val name: String,
val icon: FeedGroupIcon, val icon: FeedGroupIcon
) : BindableItem<FeedGroupCardItemBinding>() { ) : BindableItem<FeedGroupCardItemBinding>() {
constructor (feedGroupEntity: FeedGroupEntity) : this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon) constructor (feedGroupEntity: FeedGroupEntity) : this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon)

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.local.subscription.item package org.schabi.newpipe.local.subscription.item
import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import android.view.View import android.view.View
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
@ -10,54 +9,77 @@ import com.xwray.groupie.viewbinding.BindableItem
import com.xwray.groupie.viewbinding.GroupieViewHolder import com.xwray.groupie.viewbinding.GroupieViewHolder
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.FeedItemCarouselBinding import org.schabi.newpipe.databinding.FeedItemCarouselBinding
import org.schabi.newpipe.local.subscription.decoration.FeedGroupCarouselDecoration import org.schabi.newpipe.util.DeviceUtils
import java.lang.Integer.max
class FeedGroupCarouselItem( class FeedGroupCarouselItem(
context: Context,
private val carouselAdapter: GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>, private val carouselAdapter: GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>,
private var listView: Int, var listViewMode: Boolean
private var isGridLayout: Boolean
) : BindableItem<FeedItemCarouselBinding>() { ) : BindableItem<FeedItemCarouselBinding>() {
private val feedGroupCarouselDecoration = FeedGroupCarouselDecoration(context) companion object {
const val PAYLOAD_UPDATE_LIST_VIEW_MODE = 2
}
private var linearLayoutManager: LinearLayoutManager? = null private var carouselLayoutManager: LinearLayoutManager? = null
private var listState: Parcelable? = null private var listState: Parcelable? = null
override fun getLayout() = R.layout.feed_item_carousel override fun getLayout() = R.layout.feed_item_carousel
fun onSaveInstanceState(): Parcelable? { fun onSaveInstanceState(): Parcelable? {
listState = linearLayoutManager?.onSaveInstanceState() listState = carouselLayoutManager?.onSaveInstanceState()
return listState return listState
} }
fun onRestoreInstanceState(state: Parcelable?) { fun onRestoreInstanceState(state: Parcelable?) {
linearLayoutManager?.onRestoreInstanceState(state) carouselLayoutManager?.onRestoreInstanceState(state)
listState = state listState = state
} }
override fun initializeViewBinding(view: View): FeedItemCarouselBinding { override fun initializeViewBinding(view: View): FeedItemCarouselBinding {
val viewHolder = FeedItemCarouselBinding.bind(view) val viewBinding = FeedItemCarouselBinding.bind(view)
updateViewMode(viewBinding)
return viewBinding
}
linearLayoutManager = LinearLayoutManager(view.context, listView, false) override fun bind(
viewBinding: FeedItemCarouselBinding,
viewHolder.recyclerView.apply { position: Int,
layoutManager = linearLayoutManager payloads: MutableList<Any>
adapter = carouselAdapter ) {
addItemDecoration(feedGroupCarouselDecoration) if (payloads.contains(PAYLOAD_UPDATE_LIST_VIEW_MODE)) {
updateViewMode(viewBinding)
return
} }
if (isGridLayout)
viewHolder.recyclerView.setLayoutManager(GridLayoutManager(view.context, 3)) super.bind(viewBinding, position, payloads)
return viewHolder
} }
override fun bind(viewBinding: FeedItemCarouselBinding, position: Int) { override fun bind(viewBinding: FeedItemCarouselBinding, position: Int) {
viewBinding.recyclerView.apply { adapter = carouselAdapter } viewBinding.recyclerView.apply { adapter = carouselAdapter }
linearLayoutManager?.onRestoreInstanceState(listState) carouselLayoutManager?.onRestoreInstanceState(listState)
} }
override fun unbind(viewHolder: GroupieViewHolder<FeedItemCarouselBinding>) { override fun unbind(viewHolder: GroupieViewHolder<FeedItemCarouselBinding>) {
super.unbind(viewHolder) super.unbind(viewHolder)
listState = carouselLayoutManager?.onSaveInstanceState()
}
listState = linearLayoutManager?.onSaveInstanceState() private fun updateViewMode(viewBinding: FeedItemCarouselBinding) {
viewBinding.recyclerView.apply { adapter = carouselAdapter }
val context = viewBinding.root.context
carouselLayoutManager = if (listViewMode) {
LinearLayoutManager(context)
} else {
GridLayoutManager(
context,
max(1, viewBinding.recyclerView.width / DeviceUtils.dpToPx(112, context))
)
}
viewBinding.recyclerView.apply {
layoutManager = carouselLayoutManager
adapter = carouselAdapter
}
} }
} }

View File

@ -0,0 +1,50 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
import androidx.core.view.isVisible
import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.SubscriptionGroupsHeaderBinding
class GroupsHeader(
private val title: String,
private val onSortClicked: () -> Unit,
private val onToggleListViewModeClicked: () -> Unit,
var showSortButton: Boolean = true,
var listViewMode: Boolean = true
) : BindableItem<SubscriptionGroupsHeaderBinding>() {
companion object {
const val PAYLOAD_UPDATE_ICONS = 1
}
override fun getLayout(): Int = R.layout.subscription_groups_header
override fun bind(
viewBinding: SubscriptionGroupsHeaderBinding,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.contains(PAYLOAD_UPDATE_ICONS)) {
updateIcons(viewBinding)
return
}
super.bind(viewBinding, position, payloads)
}
override fun bind(viewBinding: SubscriptionGroupsHeaderBinding, position: Int) {
viewBinding.headerTitle.text = title
viewBinding.headerSort.setOnClickListener { onSortClicked() }
viewBinding.headerToggleViewMode.setOnClickListener { onToggleListViewModeClicked() }
updateIcons(viewBinding)
}
override fun initializeViewBinding(view: View) = SubscriptionGroupsHeaderBinding.bind(view)
private fun updateIcons(viewBinding: SubscriptionGroupsHeaderBinding) {
viewBinding.headerToggleViewMode.setImageResource(
if (listViewMode) R.drawable.ic_apps else R.drawable.ic_list
)
viewBinding.headerSort.isVisible = showSortButton
}
}

View File

@ -0,0 +1,17 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.SubscriptionHeaderBinding
class Header(private val title: String) : BindableItem<SubscriptionHeaderBinding>() {
override fun getLayout(): Int = R.layout.subscription_header
override fun bind(viewBinding: SubscriptionHeaderBinding, position: Int) {
viewBinding.root.text = title
}
override fun initializeViewBinding(view: View) = SubscriptionHeaderBinding.bind(view)
}

View File

@ -1,56 +0,0 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View
import android.view.View.OnClickListener
import androidx.annotation.DrawableRes
import androidx.core.view.isVisible
import com.xwray.groupie.viewbinding.BindableItem
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.HeaderWithMenuItemBinding
class HeaderWithMenuItem(
val title: String,
@DrawableRes val itemIcon: Int = 0,
@DrawableRes val itemIconListView: Int = 0,
var showMenuItem: Boolean = true,
private val onClickListener: (() -> Unit)? = null,
private val listViewOnClickListener: (() -> Unit)? = null,
private val menuItemOnClickListener: (() -> Unit)? = null
) : BindableItem<HeaderWithMenuItemBinding>() {
companion object {
const val PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM = 1
}
override fun getLayout(): Int = R.layout.header_with_menu_item
override fun bind(viewBinding: HeaderWithMenuItemBinding, position: Int, payloads: MutableList<Any>) {
if (payloads.contains(PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM)) {
updateMenuItemVisibility(viewBinding)
return
}
super.bind(viewBinding, position, payloads)
}
override fun bind(viewBinding: HeaderWithMenuItemBinding, position: Int) {
viewBinding.headerTitle.text = title
viewBinding.headerMenuItem2.setImageResource(itemIcon)
viewBinding.headerMenuItem.setImageResource(itemIconListView)
val listener = onClickListener?.let { OnClickListener { onClickListener.invoke() } }
viewBinding.root.setOnClickListener(listener)
val listViewListener = listViewOnClickListener?.let { OnClickListener { listViewOnClickListener.invoke() } }
viewBinding.headerMenuItem2.setOnClickListener(listViewListener)
val menuItemListener = menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } }
viewBinding.headerMenuItem.setOnClickListener(menuItemListener)
updateMenuItemVisibility(viewBinding)
}
override fun initializeViewBinding(view: View) = HeaderWithMenuItemBinding.bind(view)
private fun updateMenuItemVisibility(viewBinding: HeaderWithMenuItemBinding) {
viewBinding.headerMenuItem.isVisible = showMenuItem
}
}

View File

@ -2,9 +2,10 @@
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="388dp" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="4dp"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground" android:foreground="?attr/selectableItemBackground"
@ -20,9 +21,10 @@
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="14dp" android:layout_width="36dp"
android:layout_height="14dp" android:layout_height="36dp"
android:layout_gravity="center" android:layout_gravity="center"
android:padding="4dp"
android:scaleType="centerInside" android:scaleType="centerInside"
android:src="@drawable/ic_add" android:src="@drawable/ic_add"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
@ -31,15 +33,13 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:text="@string/feed_create_new_group_button_title" android:text="@string/feed_create_new_group_button_title"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/colorAccent" android:textColor="?attr/colorAccent"
android:textSize="10sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold" />
tools:ignore="SmallSp" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -2,9 +2,10 @@
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="112dp" android:layout_width="match_parent"
android:layout_height="75dp" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="4dp"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground" android:foreground="?attr/selectableItemBackground"
@ -14,15 +15,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/dashed_border" android:background="?attr/dashed_border"
android:gravity="center" android:orientation="horizontal">
android:orientation="vertical"
android:padding="4dp">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="14dp" android:layout_width="48dp"
android:layout_height="14dp" android:layout_height="48dp"
android:layout_gravity="center" android:layout_gravity="center"
android:padding="8dp"
android:scaleType="centerInside" android:scaleType="centerInside"
android:src="@drawable/ic_add" android:src="@drawable/ic_add"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
@ -31,15 +31,14 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="2dp" android:layout_gravity="center_vertical"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:padding="10dp"
android:text="@string/feed_create_new_group_button_title" android:text="@string/feed_create_new_group_button_title"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="?attr/colorAccent" android:textColor="?attr/colorAccent"
android:textSize="10sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold" />
tools:ignore="SmallSp" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -2,44 +2,45 @@
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="388dp" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground" android:foreground="?attr/selectableItemBackground"
android:layout_margin="4dp"
app:cardBackgroundColor="?attr/card_item_background_color"> app:cardBackgroundColor="?attr/card_item_background_color">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal"> android:orientation="vertical"
android:paddingTop="2dp">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="64dp" android:layout_width="36dp"
android:layout_height="48dp" android:layout_height="0dp"
android:layout_gravity="center" android:layout_gravity="center"
android:paddingTop="2dp" android:layout_weight="1"
android:paddingBottom="2dp" android:padding="4dp"
android:scaleType="centerInside" android:scaleType="fitCenter"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:src="@drawable/ic_fastfood" /> tools:src="@drawable/ic_fastfood" />
<org.schabi.newpipe.views.NewPipeTextView <org.schabi.newpipe.views.NewPipeTextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:ellipsize="end"
android:background="?attr/card_item_contrast_color" android:background="?attr/card_item_contrast_color"
android:gravity="center_vertical" android:ellipsize="end"
android:maxLines="1" android:gravity="center"
android:padding="10dp" android:maxLines="2"
android:padding="2dp"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="?attr/colorAccent" android:textColor="?attr/colorAccent"
android:textSize="16sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="ALL" /> tools:text="ALL" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -2,9 +2,10 @@
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="112dp" android:layout_width="match_parent"
android:layout_height="75dp" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center_vertical"
android:layout_margin="4dp"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground" android:foreground="?attr/selectableItemBackground"
@ -12,38 +13,34 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="horizontal">
android:paddingTop="2dp">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="18dp" android:layout_width="48dp"
android:layout_height="0dp" android:layout_height="48dp"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_weight="1" android:padding="8dp"
android:paddingTop="2dp" android:scaleType="centerInside"
android:paddingBottom="2dp"
android:scaleType="fitCenter"
android:scaleX="1.5"
android:scaleY="1.5"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:src="@drawable/ic_fastfood" /> tools:src="@drawable/ic_fastfood" />
<org.schabi.newpipe.views.NewPipeTextView <org.schabi.newpipe.views.NewPipeTextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="?attr/card_item_contrast_color" android:background="?attr/card_item_contrast_color"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center" android:gravity="center_vertical"
android:maxLines="2" android:maxLines="1"
android:padding="2dp" android:padding="10dp"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="?attr/colorAccent" android:textColor="?attr/colorAccent"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="SmallSp" tools:text="All" />
tools:text="ALL" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -3,4 +3,5 @@
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dp"
android:scrollbars="none" /> android:scrollbars="none" />

View File

@ -23,18 +23,18 @@
tools:text="Header" /> tools:text="Header" />
<ImageButton <ImageButton
android:id="@+id/header_menu_item2" android:id="@+id/header_toggle_view_mode"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
tools:src="@drawable/ic_bookmark" /> tools:src="@drawable/ic_apps" />
<ImageButton <ImageButton
android:id="@+id/header_menu_item" android:id="@+id/header_sort"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
tools:src="@drawable/ic_bookmark" /> android:src="@drawable/ic_sort" />
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<org.schabi.newpipe.views.NewPipeTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:paddingLeft="16dp"
android:paddingTop="12dp"
android:paddingRight="16dp"
android:paddingBottom="12dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:textStyle="bold"
tools:text="Header" />