Fix grid/list toggle implementation of feed
This commit is contained in:
parent
8b9db369f6
commit
0e169951f7
|
@ -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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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>
|
|
@ -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" />
|
Loading…
Reference in New Issue