mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
refactor: rework RecyclerViews to set adapter once (#6971)
This commit is contained in:
parent
87aca083a6
commit
0cf7abb07d
@ -2,21 +2,29 @@ package com.github.libretube.ui.activities
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.databinding.VideoTagRowBinding
|
||||
import com.github.libretube.ui.viewholders.VideoTagsViewHolder
|
||||
|
||||
class VideoTagsAdapter(private val tags: List<String>) :
|
||||
RecyclerView.Adapter<VideoTagsViewHolder>() {
|
||||
class VideoTagsAdapter :
|
||||
ListAdapter<String, VideoTagsViewHolder>(object : DiffUtil.ItemCallback<String>() {
|
||||
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoTagsViewHolder {
|
||||
val binding = VideoTagRowBinding.inflate(LayoutInflater.from(parent.context))
|
||||
return VideoTagsViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount() = tags.size
|
||||
|
||||
override fun onBindViewHolder(holder: VideoTagsViewHolder, position: Int) {
|
||||
val tag = tags[position]
|
||||
val tag = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
tagText.text = tag
|
||||
root.setOnClickListener {
|
||||
|
@ -47,13 +47,16 @@ class WelcomeActivity : BaseActivity() {
|
||||
val binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.instancesRecycler.layoutManager = LinearLayoutManager(this@WelcomeActivity)
|
||||
val adapter = InstancesAdapter(viewModel.selectedInstanceIndex.value) { index ->
|
||||
viewModel.selectedInstanceIndex.value = index
|
||||
binding.okay.alpha = 1f
|
||||
}
|
||||
binding.instancesRecycler.adapter = adapter
|
||||
|
||||
// ALl the binding values are optional due to two different possible layouts (normal, landscape)
|
||||
viewModel.instances.observe(this) { instances ->
|
||||
binding.instancesRecycler.layoutManager = LinearLayoutManager(this@WelcomeActivity)
|
||||
binding.instancesRecycler.adapter = InstancesAdapter(ImmutableList.copyOf(instances), viewModel.selectedInstanceIndex.value) { index ->
|
||||
viewModel.selectedInstanceIndex.value = index
|
||||
binding.okay.alpha = 1f
|
||||
}
|
||||
adapter.submitList(ImmutableList.copyOf(instances))
|
||||
binding.progress.isGone = true
|
||||
}
|
||||
viewModel.fetchInstances()
|
||||
|
@ -2,25 +2,35 @@ package com.github.libretube.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.databinding.AddChannelToGroupRowBinding
|
||||
import com.github.libretube.db.obj.SubscriptionGroup
|
||||
import com.github.libretube.ui.viewholders.AddChannelToGroupViewHolder
|
||||
|
||||
class AddChannelToGroupAdapter(
|
||||
private val channelGroups: MutableList<SubscriptionGroup>,
|
||||
private val channelId: String
|
||||
) : RecyclerView.Adapter<AddChannelToGroupViewHolder>() {
|
||||
) : ListAdapter<SubscriptionGroup, AddChannelToGroupViewHolder>(object: DiffUtil.ItemCallback<SubscriptionGroup>() {
|
||||
override fun areItemsTheSame(oldItem: SubscriptionGroup, newItem: SubscriptionGroup): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: SubscriptionGroup,
|
||||
newItem: SubscriptionGroup
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddChannelToGroupViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = AddChannelToGroupRowBinding.inflate(layoutInflater, parent, false)
|
||||
return AddChannelToGroupViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount() = channelGroups.size
|
||||
|
||||
override fun onBindViewHolder(holder: AddChannelToGroupViewHolder, position: Int) {
|
||||
val channelGroup = channelGroups[position]
|
||||
val channelGroup = getItem(holder.bindingAdapterPosition)
|
||||
|
||||
holder.binding.apply {
|
||||
groupName.text = channelGroup.name
|
||||
|
@ -9,7 +9,8 @@ import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.VideoRowBinding
|
||||
@ -37,9 +38,21 @@ import kotlin.io.path.fileSize
|
||||
class DownloadsAdapter(
|
||||
private val context: Context,
|
||||
private val downloadTab: DownloadTab,
|
||||
private val downloads: MutableList<DownloadWithItems>,
|
||||
private val toggleDownload: (DownloadWithItems) -> Boolean
|
||||
) : RecyclerView.Adapter<DownloadsViewHolder>() {
|
||||
) : ListAdapter<DownloadWithItems, DownloadsViewHolder>(object :
|
||||
DiffUtil.ItemCallback<DownloadWithItems>() {
|
||||
override fun areItemsTheSame(oldItem: DownloadWithItems, newItem: DownloadWithItems): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: DownloadWithItems,
|
||||
newItem: DownloadWithItems
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadsViewHolder {
|
||||
val binding = VideoRowBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
@ -51,8 +64,8 @@ class DownloadsAdapter(
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: DownloadsViewHolder, position: Int) {
|
||||
val download = downloads[position].download
|
||||
val items = downloads[position].downloadItems
|
||||
val download = getItem(holder.bindingAdapterPosition).download
|
||||
val items = getItem(holder.bindingAdapterPosition).downloadItems
|
||||
|
||||
holder.binding.apply {
|
||||
fileSize.isVisible = true
|
||||
@ -96,7 +109,7 @@ class DownloadsAdapter(
|
||||
}
|
||||
|
||||
progressBar.setOnClickListener {
|
||||
val isDownloading = toggleDownload(downloads[position])
|
||||
val isDownloading = toggleDownload(getItem(holder.bindingAdapterPosition))
|
||||
|
||||
resumePauseBtn.setImageResource(
|
||||
if (isDownloading) {
|
||||
@ -113,7 +126,11 @@ class DownloadsAdapter(
|
||||
intent.putExtra(IntentData.videoId, download.videoId)
|
||||
root.context.startActivity(intent)
|
||||
} else {
|
||||
BackgroundHelper.playOnBackgroundOffline(root.context, download.videoId, downloadTab)
|
||||
BackgroundHelper.playOnBackgroundOffline(
|
||||
root.context,
|
||||
download.videoId,
|
||||
downloadTab
|
||||
)
|
||||
NavigationHelper.openAudioPlayerFragment(root.context, offlinePlayer = true)
|
||||
}
|
||||
}
|
||||
@ -152,11 +169,9 @@ class DownloadsAdapter(
|
||||
.show()
|
||||
}
|
||||
|
||||
fun itemAt(index: Int) = downloads[index]
|
||||
|
||||
fun deleteDownload(position: Int) {
|
||||
val download = downloads[position].download
|
||||
val items = downloads[position].downloadItems
|
||||
val download = getItem(position).download
|
||||
val items = getItem(position).downloadItems
|
||||
|
||||
items.forEach {
|
||||
it.path.deleteIfExists()
|
||||
@ -168,9 +183,9 @@ class DownloadsAdapter(
|
||||
runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
||||
}
|
||||
downloads.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
submitList(currentList.toMutableList().also {
|
||||
it.removeAt(position)
|
||||
})
|
||||
}
|
||||
|
||||
fun restoreItem(position: Int) {
|
||||
@ -178,6 +193,4 @@ class DownloadsAdapter(
|
||||
notifyItemRemoved(position)
|
||||
notifyItemInserted(position)
|
||||
}
|
||||
|
||||
override fun getItemCount() = downloads.size
|
||||
}
|
||||
|
@ -3,18 +3,26 @@ package com.github.libretube.ui.adapters
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.PipedInstance
|
||||
import com.github.libretube.databinding.InstanceRowBinding
|
||||
import com.github.libretube.ui.viewholders.InstancesViewHolder
|
||||
import com.google.common.collect.ImmutableList
|
||||
|
||||
class InstancesAdapter(
|
||||
private val instances: ImmutableList<PipedInstance>,
|
||||
initialSelectionApiIndex: Int?,
|
||||
private val onSelectInstance: (index: Int) -> Unit
|
||||
) : RecyclerView.Adapter<InstancesViewHolder>() {
|
||||
) : ListAdapter<PipedInstance, InstancesViewHolder>(object: DiffUtil.ItemCallback<PipedInstance>() {
|
||||
override fun areItemsTheSame(oldItem: PipedInstance, newItem: PipedInstance): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: PipedInstance, newItem: PipedInstance): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
private var selectedInstanceIndex = initialSelectionApiIndex?.takeIf { it >= 0 }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstancesViewHolder {
|
||||
@ -23,11 +31,9 @@ class InstancesAdapter(
|
||||
return InstancesViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount() = instances.size
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: InstancesViewHolder, position: Int) {
|
||||
val instance = instances[position]
|
||||
val instance = getItem(holder.bindingAdapterPosition)
|
||||
|
||||
holder.binding.apply {
|
||||
var instanceText = "${instance.name} ${instance.locations}"
|
||||
|
@ -3,7 +3,9 @@ package com.github.libretube.ui.adapters
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.LegacySubscriptionChannelBinding
|
||||
import com.github.libretube.extensions.toID
|
||||
@ -13,9 +15,17 @@ import com.github.libretube.ui.base.BaseActivity
|
||||
import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
|
||||
import com.github.libretube.ui.viewholders.LegacySubscriptionViewHolder
|
||||
|
||||
class LegacySubscriptionAdapter(
|
||||
private val subscriptions: List<com.github.libretube.api.obj.Subscription>
|
||||
) : RecyclerView.Adapter<LegacySubscriptionViewHolder>() {
|
||||
class LegacySubscriptionAdapter : ListAdapter<Subscription, LegacySubscriptionViewHolder>(object :
|
||||
DiffUtil.ItemCallback<Subscription>() {
|
||||
override fun areItemsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
@ -27,7 +37,7 @@ class LegacySubscriptionAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LegacySubscriptionViewHolder, position: Int) {
|
||||
val subscription = subscriptions[position]
|
||||
val subscription = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
channelName.text = subscription.name
|
||||
ImageHelper.loadImage(
|
||||
@ -51,6 +61,4 @@ class LegacySubscriptionAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = subscriptions.size
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.PlaylistBookmarkRowBinding
|
||||
@ -23,9 +24,17 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PlaylistBookmarkAdapter(
|
||||
private val bookmarks: List<PlaylistBookmark>,
|
||||
private val bookmarkMode: BookmarkMode = BookmarkMode.FRAGMENT
|
||||
) : RecyclerView.Adapter<PlaylistBookmarkViewHolder>() {
|
||||
) : ListAdapter<PlaylistBookmark, PlaylistBookmarkViewHolder>(object: DiffUtil.ItemCallback<PlaylistBookmark>() {
|
||||
override fun areItemsTheSame(oldItem: PlaylistBookmark, newItem: PlaylistBookmark): Boolean {
|
||||
return oldItem.playlistId == newItem.playlistId
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: PlaylistBookmark, newItem: PlaylistBookmark): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistBookmarkViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
return when (bookmarkMode) {
|
||||
@ -39,8 +48,6 @@ class PlaylistBookmarkAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = bookmarks.size
|
||||
|
||||
private fun showPlaylistOptions(context: Context, bookmark: PlaylistBookmark) {
|
||||
val sheet = PlaylistOptionsBottomSheet()
|
||||
sheet.arguments = bundleOf(
|
||||
@ -54,7 +61,7 @@ class PlaylistBookmarkAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PlaylistBookmarkViewHolder, position: Int) {
|
||||
val bookmark = bookmarks[position]
|
||||
val bookmark = getItem(holder.bindingAdapterPosition)
|
||||
holder.playlistBookmarkBinding?.apply {
|
||||
ImageHelper.loadImage(bookmark.thumbnailUrl, thumbnail)
|
||||
playlistName.text = bookmark.playlistName
|
||||
|
@ -3,7 +3,8 @@ package com.github.libretube.ui.adapters
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Playlists
|
||||
import com.github.libretube.constants.IntentData
|
||||
@ -17,18 +18,18 @@ import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet.Companion.PLAYL
|
||||
import com.github.libretube.ui.viewholders.PlaylistsViewHolder
|
||||
|
||||
class PlaylistsAdapter(
|
||||
private val playlists: MutableList<Playlists>,
|
||||
private val playlistType: PlaylistType
|
||||
) : RecyclerView.Adapter<PlaylistsViewHolder>() {
|
||||
|
||||
override fun getItemCount() = playlists.size
|
||||
|
||||
fun updateItems(newItems: List<Playlists>) {
|
||||
val oldSize = playlists.size
|
||||
playlists.addAll(newItems)
|
||||
notifyItemRangeInserted(oldSize, playlists.size)
|
||||
) : ListAdapter<Playlists, PlaylistsViewHolder>(object : DiffUtil.ItemCallback<Playlists>() {
|
||||
override fun areItemsTheSame(oldItem: Playlists, newItem: Playlists): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Playlists, newItem: Playlists): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistsViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = PlaylistsRowBinding.inflate(layoutInflater, parent, false)
|
||||
@ -36,7 +37,7 @@ class PlaylistsAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PlaylistsViewHolder, position: Int) {
|
||||
val playlist = playlists[position]
|
||||
val playlist = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
// set imageview drawable as empty playlist if imageview empty
|
||||
if (playlist.thumbnail.orEmpty().split("/").size <= 4) {
|
||||
@ -80,7 +81,7 @@ class PlaylistsAdapter(
|
||||
|
||||
if (isPlaylistToBeDeleted) {
|
||||
// try to refresh the playlists in the library on deletion success
|
||||
onDelete(position, root.context as BaseActivity)
|
||||
onDelete(position)
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,11 +100,10 @@ class PlaylistsAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(position: Int, activity: BaseActivity) {
|
||||
playlists.removeAt(position)
|
||||
activity.runOnUiThread {
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
private fun onDelete(position: Int) {
|
||||
val newList = currentList.toMutableList().also {
|
||||
it.removeAt(position)
|
||||
}
|
||||
submitList(newList)
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ package com.github.libretube.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.databinding.SuggestionRowBinding
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.db.obj.SearchHistoryItem
|
||||
@ -13,13 +13,18 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class SearchHistoryAdapter(
|
||||
private var historyList: List<String>,
|
||||
private val searchView: SearchView
|
||||
) :
|
||||
RecyclerView.Adapter<SuggestionsViewHolder>() {
|
||||
private val onRootClickListener: (String) -> Unit,
|
||||
private val onArrowClickListener: (String) -> Unit,
|
||||
) : ListAdapter<String, SuggestionsViewHolder>(object: DiffUtil.ItemCallback<String>() {
|
||||
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getItemCount() = historyList.size
|
||||
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SuggestionsViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = SuggestionRowBinding.inflate(layoutInflater, parent, false)
|
||||
@ -27,26 +32,28 @@ class SearchHistoryAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SuggestionsViewHolder, position: Int) {
|
||||
val historyQuery = historyList[position]
|
||||
val historyQuery = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
suggestionText.text = historyQuery
|
||||
|
||||
deleteHistory.isVisible = true
|
||||
|
||||
deleteHistory.setOnClickListener {
|
||||
historyList -= historyQuery
|
||||
val updatedList = currentList.toMutableList().also {
|
||||
it.remove(historyQuery)
|
||||
}
|
||||
runBlocking(Dispatchers.IO) {
|
||||
Database.searchHistoryDao().delete(SearchHistoryItem(historyQuery))
|
||||
}
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
|
||||
submitList(updatedList)
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
searchView.setQuery(historyQuery, true)
|
||||
onRootClickListener(historyQuery)
|
||||
}
|
||||
arrow.setOnClickListener {
|
||||
searchView.setQuery(historyQuery, false)
|
||||
onArrowClickListener(historyQuery)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,24 @@ package com.github.libretube.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.databinding.SuggestionRowBinding
|
||||
import com.github.libretube.ui.viewholders.SuggestionsViewHolder
|
||||
|
||||
class SearchSuggestionsAdapter(
|
||||
private var suggestionsList: List<String>,
|
||||
private val searchView: SearchView
|
||||
) :
|
||||
RecyclerView.Adapter<SuggestionsViewHolder>() {
|
||||
private val onRootClickListener: (String) -> Unit,
|
||||
private val onArrowClickListener: (String) -> Unit,
|
||||
) : ListAdapter<String, SuggestionsViewHolder>(object: DiffUtil.ItemCallback<String>() {
|
||||
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getItemCount() = suggestionsList.size
|
||||
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SuggestionsViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
@ -22,14 +28,14 @@ class SearchSuggestionsAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SuggestionsViewHolder, position: Int) {
|
||||
val suggestion = suggestionsList[position]
|
||||
val suggestion = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
suggestionText.text = suggestion
|
||||
root.setOnClickListener {
|
||||
searchView.setQuery(suggestion, true)
|
||||
onRootClickListener(suggestion)
|
||||
}
|
||||
arrow.setOnClickListener {
|
||||
searchView.setQuery(suggestion, false)
|
||||
onArrowClickListener(suggestion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ package com.github.libretube.ui.adapters
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.ChannelSubscriptionRowBinding
|
||||
@ -15,12 +16,20 @@ import com.github.libretube.ui.extensions.setupSubscriptionButton
|
||||
import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
|
||||
import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder
|
||||
|
||||
class SubscriptionChannelAdapter(
|
||||
private val subscriptions: MutableList<Subscription>
|
||||
) : RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||
class SubscriptionChannelAdapter : ListAdapter<Subscription, SubscriptionChannelViewHolder>(object :
|
||||
DiffUtil.ItemCallback<Subscription>() {
|
||||
override fun areItemsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
private var visibleCount = 20
|
||||
|
||||
override fun getItemCount() = minOf(visibleCount, subscriptions.size)
|
||||
override fun getItemCount() = minOf(visibleCount, currentList.size)
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
@ -33,13 +42,13 @@ class SubscriptionChannelAdapter(
|
||||
|
||||
fun updateItems() {
|
||||
val oldSize = visibleCount
|
||||
visibleCount += minOf(10, subscriptions.size - oldSize)
|
||||
visibleCount += minOf(10, currentList.size - oldSize)
|
||||
if (visibleCount == oldSize) return
|
||||
notifyItemRangeInserted(oldSize, visibleCount)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
|
||||
val subscription = subscriptions[position]
|
||||
val subscription = getItem(holder.bindingAdapterPosition)
|
||||
|
||||
holder.binding.apply {
|
||||
subscriptionChannelName.text = subscription.name
|
||||
|
@ -2,7 +2,8 @@ package com.github.libretube.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.databinding.SubscriptionGroupChannelRowBinding
|
||||
import com.github.libretube.db.obj.SubscriptionGroup
|
||||
@ -12,10 +13,18 @@ import com.github.libretube.helpers.NavigationHelper
|
||||
import com.github.libretube.ui.viewholders.SubscriptionGroupChannelRowViewHolder
|
||||
|
||||
class SubscriptionGroupChannelsAdapter(
|
||||
private val channels: List<Subscription>,
|
||||
private val group: SubscriptionGroup,
|
||||
private val onGroupChanged: (SubscriptionGroup) -> Unit
|
||||
) : RecyclerView.Adapter<SubscriptionGroupChannelRowViewHolder>() {
|
||||
) : ListAdapter<Subscription, SubscriptionGroupChannelRowViewHolder>(object: DiffUtil.ItemCallback<Subscription>() {
|
||||
override fun areItemsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Subscription, newItem: Subscription): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
@ -25,10 +34,8 @@ class SubscriptionGroupChannelsAdapter(
|
||||
return SubscriptionGroupChannelRowViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount() = channels.size
|
||||
|
||||
override fun onBindViewHolder(holder: SubscriptionGroupChannelRowViewHolder, position: Int) {
|
||||
val channel = channels[position]
|
||||
val channel = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
root.setOnClickListener {
|
||||
NavigationHelper.navigateChannel(root.context, channel.url)
|
||||
|
@ -8,8 +8,9 @@ import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView.LayoutManager
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.constants.IntentData
|
||||
@ -36,29 +37,38 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class VideosAdapter(
|
||||
private val streamItems: MutableList<StreamItem>,
|
||||
private val forceMode: LayoutMode = LayoutMode.RESPECT_PREF
|
||||
) : RecyclerView.Adapter<VideosViewHolder>() {
|
||||
override fun getItemCount() = streamItems.size
|
||||
) : ListAdapter<StreamItem, VideosViewHolder>(object: DiffUtil.ItemCallback<StreamItem>() {
|
||||
override fun areItemsTheSame(oldItem: StreamItem, newItem: StreamItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: StreamItem, newItem: StreamItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (streamItems[position].type == CAUGHT_UP_STREAM_TYPE) CAUGHT_UP_TYPE else NORMAL_TYPE
|
||||
return if (currentList[position].type == CAUGHT_UP_STREAM_TYPE) CAUGHT_UP_TYPE else NORMAL_TYPE
|
||||
}
|
||||
|
||||
fun insertItems(newItems: List<StreamItem>) {
|
||||
val feedSize = streamItems.size
|
||||
streamItems.addAll(newItems)
|
||||
notifyItemRangeInserted(feedSize, newItems.size)
|
||||
val updatedList = currentList.toMutableList().also {
|
||||
it.addAll(newItems)
|
||||
}
|
||||
|
||||
submitList(updatedList)
|
||||
}
|
||||
|
||||
fun removeItemById(videoId: String) {
|
||||
val index = streamItems.indexOfFirst {
|
||||
val index = currentList.indexOfFirst {
|
||||
it.url?.toID() == videoId
|
||||
}.takeIf { it > 0 } ?: return
|
||||
streamItems.removeAt(index)
|
||||
val updatedList = currentList.toMutableList().also {
|
||||
it.removeAt(index)
|
||||
}
|
||||
|
||||
notifyItemRemoved(index)
|
||||
notifyItemRangeChanged(index, itemCount)
|
||||
submitList(updatedList)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideosViewHolder {
|
||||
@ -90,7 +100,7 @@ class VideosAdapter(
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: VideosViewHolder, position: Int) {
|
||||
val video = streamItems[position]
|
||||
val video = getItem(holder.bindingAdapterPosition)
|
||||
val videoId = video.url.orEmpty().toID()
|
||||
|
||||
val context = (
|
||||
|
@ -5,7 +5,8 @@ import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.VideoRowBinding
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
@ -24,27 +25,34 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class WatchHistoryAdapter(
|
||||
private val watchHistory: MutableList<WatchHistoryItem>
|
||||
) :
|
||||
RecyclerView.Adapter<WatchHistoryViewHolder>() {
|
||||
class WatchHistoryAdapter : ListAdapter<WatchHistoryItem, WatchHistoryViewHolder>(object :
|
||||
DiffUtil.ItemCallback<WatchHistoryItem>() {
|
||||
override fun areItemsTheSame(oldItem: WatchHistoryItem, newItem: WatchHistoryItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun getItemCount() = watchHistory.size
|
||||
override fun areContentsTheSame(oldItem: WatchHistoryItem, newItem: WatchHistoryItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
fun removeFromWatchHistory(position: Int) {
|
||||
val history = watchHistory[position]
|
||||
val history = getItem(position)
|
||||
runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.watchHistoryDao().delete(history)
|
||||
}
|
||||
watchHistory.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
val updatedList = currentList.toMutableList().also {
|
||||
it.removeAt(position)
|
||||
}
|
||||
submitList(updatedList)
|
||||
}
|
||||
|
||||
fun insertItems(items: List<WatchHistoryItem>) {
|
||||
val oldSize = itemCount
|
||||
this.watchHistory.addAll(items)
|
||||
notifyItemRangeInserted(oldSize, itemCount)
|
||||
val updatedList = currentList.toMutableList().also {
|
||||
it.addAll(items)
|
||||
}
|
||||
submitList(updatedList)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
|
||||
@ -54,7 +62,7 @@ class WatchHistoryAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: WatchHistoryViewHolder, position: Int) {
|
||||
val video = watchHistory[position]
|
||||
val video = getItem(holder.bindingAdapterPosition)
|
||||
holder.binding.apply {
|
||||
videoTitle.text = video.title
|
||||
channelName.text = video.uploader
|
||||
|
@ -87,10 +87,8 @@ class ChannelContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_ch
|
||||
}
|
||||
nextPage = response.nextpage
|
||||
|
||||
val binding = _binding ?: return@launch
|
||||
searchChannelAdapter = SearchChannelAdapter()
|
||||
binding.channelRecView.adapter = searchChannelAdapter
|
||||
searchChannelAdapter?.submitList(response.content)
|
||||
val binding = _binding ?: return@launch
|
||||
binding.progressBar.isGone = true
|
||||
|
||||
isLoading = false
|
||||
@ -123,6 +121,9 @@ class ChannelContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_ch
|
||||
channelId = arguments.getString(IntentData.channelId)
|
||||
nextPage = arguments.getString(IntentData.nextPage)
|
||||
|
||||
searchChannelAdapter = SearchChannelAdapter()
|
||||
binding.channelRecView.adapter = searchChannelAdapter
|
||||
|
||||
binding.channelRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
@ -147,9 +148,10 @@ class ChannelContentFragment : DynamicLayoutManagerFragment(R.layout.fragment_ch
|
||||
|
||||
if (tabData?.data.isNullOrEmpty()) {
|
||||
channelAdapter = VideosAdapter(
|
||||
arguments.parcelableArrayList<StreamItem>(IntentData.videoList)!!,
|
||||
forceMode = VideosAdapter.Companion.LayoutMode.CHANNEL_ROW
|
||||
)
|
||||
).also {
|
||||
it.submitList(arguments.parcelableArrayList<StreamItem>(IntentData.videoList)!!)
|
||||
}
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
binding.progressBar.isGone = true
|
||||
|
||||
|
@ -236,9 +236,10 @@ class ChannelFragment : DynamicLayoutManagerFragment(R.layout.fragment_channel)
|
||||
}.attach()
|
||||
|
||||
channelAdapter = VideosAdapter(
|
||||
response.relatedStreams.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.LayoutMode.CHANNEL_ROW
|
||||
)
|
||||
).also {
|
||||
it.submitList(response.relatedStreams)
|
||||
}
|
||||
tabList.clear()
|
||||
|
||||
val tabs = listOf(ChannelTab(VIDEOS_TAB_KEY, "")) + response.tabs
|
||||
|
@ -146,6 +146,32 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentDownloadContentBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
adapter = DownloadsAdapter(requireContext(), downloadTab) {
|
||||
var isDownloading = false
|
||||
val ids = it.downloadItems
|
||||
.filter { item -> item.path.fileSize() < item.downloadSize }
|
||||
.map { item -> item.id }
|
||||
|
||||
if (!serviceConnection.isBound) {
|
||||
DownloadHelper.startDownloadService(requireContext())
|
||||
bindDownloadService(ids.toIntArray())
|
||||
return@DownloadsAdapter true
|
||||
}
|
||||
|
||||
binder?.getService()?.let { service ->
|
||||
isDownloading = ids.any { id -> service.isDownloading(id) }
|
||||
|
||||
ids.forEach { id ->
|
||||
if (isDownloading) {
|
||||
service.pause(id)
|
||||
} else {
|
||||
service.resume(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return@DownloadsAdapter isDownloading.not()
|
||||
}
|
||||
binding.downloadsRecView.adapter = adapter
|
||||
|
||||
var selectedSortType =
|
||||
PreferenceHelper.getInt(PreferenceKeys.SELECTED_DOWNLOAD_SORT_TYPE, 0)
|
||||
@ -156,7 +182,6 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
|
||||
binding.sortType.text = filterOptions[index]
|
||||
if (::adapter.isInitialized) {
|
||||
sortDownloadList(index, selectedSortType)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
selectedSortType = index
|
||||
PreferenceHelper.putInt(
|
||||
@ -178,32 +203,6 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
|
||||
|
||||
sortDownloadList(selectedSortType)
|
||||
|
||||
adapter = DownloadsAdapter(requireContext(), downloadTab, downloads) {
|
||||
var isDownloading = false
|
||||
val ids = it.downloadItems
|
||||
.filter { item -> item.path.fileSize() < item.downloadSize }
|
||||
.map { item -> item.id }
|
||||
|
||||
if (!serviceConnection.isBound) {
|
||||
DownloadHelper.startDownloadService(requireContext())
|
||||
bindDownloadService(ids.toIntArray())
|
||||
return@DownloadsAdapter true
|
||||
}
|
||||
|
||||
binder?.getService()?.let { service ->
|
||||
isDownloading = ids.any { id -> service.isDownloading(id) }
|
||||
|
||||
ids.forEach { id ->
|
||||
if (isDownloading) {
|
||||
service.pause(id)
|
||||
} else {
|
||||
service.resume(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return@DownloadsAdapter isDownloading.not()
|
||||
}
|
||||
binding.downloadsRecView.adapter = adapter
|
||||
|
||||
binding.downloadsRecView.setOnDismissListener { position ->
|
||||
adapter.showDeleteDialog(requireContext(), position)
|
||||
@ -251,10 +250,10 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
|
||||
|
||||
private fun sortDownloadList(sortType: Int, previousSortType: Int? = null) {
|
||||
if (previousSortType == null && sortType == 1) {
|
||||
downloads.reverse()
|
||||
adapter.submitList(downloads.reversed())
|
||||
}
|
||||
if (previousSortType != null && sortType != previousSortType) {
|
||||
downloads.reverse()
|
||||
adapter.submitList(downloads.reversed())
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,7 +268,7 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
lifecycleScope.launch {
|
||||
for (downloadIndex in downloads.size - 1 downTo 0) {
|
||||
val download = adapter.itemAt(downloadIndex).download
|
||||
val download = adapter.currentList[downloadIndex].download
|
||||
if (!onlyDeleteWatchedVideos || DatabaseHelper.isVideoWatched(download.videoId, download.duration ?: 0)) {
|
||||
adapter.deleteDownload(downloadIndex)
|
||||
}
|
||||
|
@ -8,9 +8,6 @@ import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.PlaylistsHelper
|
||||
@ -41,10 +38,32 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
private val subscriptionsViewModel: SubscriptionsViewModel by activityViewModels()
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
|
||||
private val trendingAdapter = VideosAdapter(forceMode = LayoutMode.TRENDING_ROW)
|
||||
private val feedAdapter = VideosAdapter(forceMode = LayoutMode.RELATED_COLUMN)
|
||||
private val watchingAdapter = VideosAdapter(forceMode = LayoutMode.RELATED_COLUMN)
|
||||
private val bookmarkAdapter = PlaylistBookmarkAdapter(PlaylistBookmarkAdapter.Companion.BookmarkMode.HOME)
|
||||
private val playlistAdapter = PlaylistsAdapter(playlistType = PlaylistsHelper.getPrivatePlaylistType())
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentHomeBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.trendingRV.adapter = trendingAdapter
|
||||
binding.featuredRV.adapter = feedAdapter
|
||||
binding.bookmarksRV.adapter = bookmarkAdapter
|
||||
binding.playlistsRV.adapter = playlistAdapter
|
||||
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
if (itemCount == 0) {
|
||||
binding.playlistsRV.isGone = true
|
||||
binding.playlistsTV.isGone = true
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.watchingRV.adapter = watchingAdapter
|
||||
|
||||
with(homeViewModel) {
|
||||
trending.observe(viewLifecycleOwner, ::showTrending)
|
||||
feed.observe(viewLifecycleOwner, ::showFeed)
|
||||
@ -123,11 +142,7 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
if (streamItems == null) return
|
||||
|
||||
makeVisible(binding.trendingRV, binding.trendingTV)
|
||||
binding.trendingRV.layoutManager = GridLayoutManager(context, 2)
|
||||
binding.trendingRV.adapter = VideosAdapter(
|
||||
streamItems.toMutableList(),
|
||||
forceMode = LayoutMode.TRENDING_ROW
|
||||
)
|
||||
trendingAdapter.submitList(streamItems)
|
||||
}
|
||||
|
||||
private fun showFeed(streamItems: List<StreamItem>?) {
|
||||
@ -138,57 +153,29 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
|
||||
val feedVideos = streamItems
|
||||
.let { DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) }
|
||||
.take(20)
|
||||
.toMutableList()
|
||||
|
||||
with(binding.featuredRV) {
|
||||
layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
|
||||
adapter = VideosAdapter(feedVideos, forceMode = LayoutMode.RELATED_COLUMN)
|
||||
}
|
||||
feedAdapter.submitList(feedVideos)
|
||||
}
|
||||
|
||||
private fun showBookmarks(bookmarks: List<PlaylistBookmark>?) {
|
||||
if (bookmarks == null) return
|
||||
|
||||
makeVisible(binding.bookmarksTV, binding.bookmarksRV)
|
||||
with(binding.bookmarksRV) {
|
||||
layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
|
||||
adapter = PlaylistBookmarkAdapter(
|
||||
bookmarks.toMutableList(),
|
||||
PlaylistBookmarkAdapter.Companion.BookmarkMode.HOME
|
||||
)
|
||||
}
|
||||
bookmarkAdapter.submitList(bookmarks)
|
||||
}
|
||||
|
||||
private fun showPlaylists(playlists: List<Playlists>?) {
|
||||
if (playlists == null) return
|
||||
|
||||
makeVisible(binding.playlistsRV, binding.playlistsTV)
|
||||
binding.playlistsRV.layoutManager = LinearLayoutManager(context)
|
||||
binding.playlistsRV.adapter = PlaylistsAdapter(
|
||||
playlists.toMutableList(),
|
||||
playlistType = PlaylistsHelper.getPrivatePlaylistType()
|
||||
)
|
||||
binding.playlistsRV.adapter?.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
if (itemCount == 0) {
|
||||
binding.playlistsRV.isGone = true
|
||||
binding.playlistsTV.isGone = true
|
||||
}
|
||||
}
|
||||
})
|
||||
playlistAdapter.submitList(playlists)
|
||||
}
|
||||
|
||||
private fun showContinueWatching(unwatchedVideos: List<StreamItem>?) {
|
||||
if (unwatchedVideos == null) return
|
||||
|
||||
makeVisible(binding.watchingRV, binding.watchingTV)
|
||||
binding.watchingRV.layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
|
||||
binding.watchingRV.adapter = VideosAdapter(
|
||||
unwatchedVideos.toMutableList(),
|
||||
forceMode = LayoutMode.RELATED_COLUMN
|
||||
)
|
||||
watchingAdapter.submitList(unwatchedVideos)
|
||||
}
|
||||
|
||||
private fun updateLoading(isLoading: Boolean) {
|
||||
|
@ -45,6 +45,9 @@ class LibraryFragment : DynamicLayoutManagerFragment(R.layout.fragment_library)
|
||||
|
||||
private val commonPlayerViewModel: CommonPlayerViewModel by activityViewModels()
|
||||
|
||||
private val playlistsAdapter = PlaylistsAdapter(PlaylistsHelper.getPrivatePlaylistType())
|
||||
private val playlistBookmarkAdapter = PlaylistBookmarkAdapter()
|
||||
|
||||
override fun setLayoutManagers(gridItems: Int) {
|
||||
_binding?.bookmarksRecView?.layoutManager = GridLayoutManager(context, gridItems.ceilHalf())
|
||||
_binding?.playlistRecView?.layoutManager = GridLayoutManager(context, gridItems.ceilHalf())
|
||||
@ -54,6 +57,18 @@ class LibraryFragment : DynamicLayoutManagerFragment(R.layout.fragment_library)
|
||||
_binding = FragmentLibraryBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.bookmarksRecView.adapter = playlistBookmarkAdapter
|
||||
// listen for playlists to become deleted
|
||||
playlistsAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
_binding?.nothingHere?.isVisible = playlistsAdapter.itemCount == 0
|
||||
_binding?.sortTV?.isVisible = playlistsAdapter.itemCount > 0
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
}
|
||||
})
|
||||
binding.playlistRecView.adapter = playlistsAdapter
|
||||
|
||||
// listen for the mini player state changing
|
||||
commonPlayerViewModel.isMiniPlayerVisible.observe(viewLifecycleOwner) {
|
||||
updateFABMargin(it)
|
||||
@ -142,7 +157,7 @@ class LibraryFragment : DynamicLayoutManagerFragment(R.layout.fragment_library)
|
||||
|
||||
binding.bookmarksCV.isVisible = bookmarks.isNotEmpty()
|
||||
if (bookmarks.isNotEmpty()) {
|
||||
binding.bookmarksRecView.adapter = PlaylistBookmarkAdapter(bookmarks)
|
||||
playlistBookmarkAdapter.submitList(bookmarks)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,23 +199,8 @@ class LibraryFragment : DynamicLayoutManagerFragment(R.layout.fragment_library)
|
||||
private fun showPlaylists(playlists: List<Playlists>) {
|
||||
val binding = _binding ?: return
|
||||
|
||||
val playlistsAdapter = PlaylistsAdapter(
|
||||
playlists.toMutableList(),
|
||||
PlaylistsHelper.getPrivatePlaylistType()
|
||||
)
|
||||
|
||||
// listen for playlists to become deleted
|
||||
playlistsAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
_binding?.nothingHere?.isVisible = playlistsAdapter.itemCount == 0
|
||||
_binding?.sortTV?.isVisible = playlistsAdapter.itemCount > 0
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
}
|
||||
})
|
||||
|
||||
binding.nothingHere.isGone = true
|
||||
binding.sortTV.isVisible = true
|
||||
binding.playlistRecView.adapter = playlistsAdapter
|
||||
playlistsAdapter.submitList(playlists)
|
||||
}
|
||||
}
|
||||
|
@ -1128,13 +1128,14 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
|
||||
if (PlayerHelper.relatedStreamsEnabled) {
|
||||
val relatedLayoutManager = binding.relatedRecView.layoutManager as LinearLayoutManager
|
||||
binding.relatedRecView.adapter = VideosAdapter(
|
||||
streams.relatedStreams.filter { !it.title.isNullOrBlank() }.toMutableList(),
|
||||
forceMode = if (relatedLayoutManager.orientation == LinearLayoutManager.HORIZONTAL) {
|
||||
VideosAdapter.Companion.LayoutMode.RELATED_COLUMN
|
||||
} else {
|
||||
VideosAdapter.Companion.LayoutMode.TRENDING_ROW
|
||||
}
|
||||
)
|
||||
).also { adapter ->
|
||||
adapter.submitList(streams.relatedStreams.filter { !it.title.isNullOrBlank() })
|
||||
}
|
||||
}
|
||||
|
||||
// update the subscribed state
|
||||
|
@ -325,6 +325,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment(R.layout.fragment_playlist
|
||||
playlistId,
|
||||
playlistType
|
||||
)
|
||||
// TODO make sure the adapter is set once in onViewCreated
|
||||
binding.playlistRecView.adapter = playlistAdapter
|
||||
|
||||
// listen for playlist items to become deleted
|
||||
|
@ -7,9 +7,10 @@ import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.map
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.constants.IntentData
|
||||
@ -34,6 +35,27 @@ class SearchSuggestionsFragment : Fragment(R.layout.fragment_search_suggestions)
|
||||
private val viewModel: SearchViewModel by activityViewModels()
|
||||
private val mainActivity get() = activity as MainActivity
|
||||
|
||||
private val historyAdapter = SearchHistoryAdapter(
|
||||
onRootClickListener = { historyQuery ->
|
||||
runCatching {
|
||||
(activity as MainActivity?)?.searchView
|
||||
}.getOrNull()?.setQuery(historyQuery, true)
|
||||
},
|
||||
onArrowClickListener = { historyQuery ->
|
||||
runCatching {
|
||||
(activity as MainActivity?)?.searchView
|
||||
}.getOrNull()?.setQuery(historyQuery, false)
|
||||
}
|
||||
)
|
||||
private val suggestionsAdapter = SearchSuggestionsAdapter(
|
||||
onRootClickListener = { suggestion ->
|
||||
(activity as MainActivity?)?.searchView?.setQuery(suggestion, true)
|
||||
},
|
||||
onArrowClickListener = { suggestion ->
|
||||
(activity as MainActivity?)?.searchView?.setQuery(suggestion, false)
|
||||
},
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.searchQuery.value = arguments?.getString(IntentData.query)
|
||||
@ -43,9 +65,15 @@ class SearchSuggestionsFragment : Fragment(R.layout.fragment_search_suggestions)
|
||||
_binding = FragmentSearchSuggestionsBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.suggestionsRecycler.layoutManager = LinearLayoutManager(requireContext()).apply {
|
||||
reverseLayout = true
|
||||
stackFromEnd = true
|
||||
viewModel.searchQuery
|
||||
.map { it.isNullOrEmpty() }
|
||||
.distinctUntilChanged()
|
||||
.observe(viewLifecycleOwner) { isQueryEmpty ->
|
||||
if (isQueryEmpty) {
|
||||
binding.suggestionsRecycler.adapter = historyAdapter
|
||||
} else if (PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_SUGGESTIONS, true)) {
|
||||
binding.suggestionsRecycler.adapter = suggestionsAdapter
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for the query to change
|
||||
@ -81,30 +109,19 @@ class SearchSuggestionsFragment : Fragment(R.layout.fragment_search_suggestions)
|
||||
return@launch
|
||||
}
|
||||
// only load the suggestions if the input field didn't get cleared yet
|
||||
val suggestionsAdapter = SearchSuggestionsAdapter(
|
||||
response.reversed(),
|
||||
(activity as MainActivity).searchView
|
||||
)
|
||||
if (isAdded && !viewModel.searchQuery.value.isNullOrEmpty()) {
|
||||
binding.suggestionsRecycler.adapter = suggestionsAdapter
|
||||
if (!viewModel.searchQuery.value.isNullOrEmpty()) {
|
||||
suggestionsAdapter.submitList(response.reversed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHistory() {
|
||||
val searchView = runCatching {
|
||||
(activity as MainActivity).searchView
|
||||
}.getOrNull()
|
||||
|
||||
lifecycleScope.launch {
|
||||
val historyList = withContext(Dispatchers.IO) {
|
||||
Database.searchHistoryDao().getAll().map { it.query }
|
||||
}
|
||||
if (historyList.isNotEmpty() && searchView != null) {
|
||||
binding.suggestionsRecycler.adapter = SearchHistoryAdapter(
|
||||
historyList,
|
||||
searchView
|
||||
)
|
||||
if (historyList.isNotEmpty()) {
|
||||
historyAdapter.submitList(historyList)
|
||||
} else {
|
||||
binding.suggestionsRecycler.isGone = true
|
||||
binding.historyEmpty.isVisible = true
|
||||
|
@ -64,10 +64,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
private var isCurrentTabSubChannels = false
|
||||
private var isAppBarFullyExpanded = true
|
||||
|
||||
private var feedAdapter: VideosAdapter? = null
|
||||
private var feedAdapter = VideosAdapter()
|
||||
private val sortedFeed: MutableList<StreamItem> = mutableListOf()
|
||||
|
||||
private var channelsAdapter: SubscriptionChannelAdapter? = null
|
||||
private var selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.FEED_SORT_ORDER, 0)
|
||||
set(value) {
|
||||
PreferenceHelper.putInt(PreferenceKeys.FEED_SORT_ORDER, value)
|
||||
@ -84,6 +83,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
private var subChannelsRecyclerViewState: Parcelable? = null
|
||||
private var subFeedRecyclerViewState: Parcelable? = null
|
||||
|
||||
private val legacySubscriptionsAdapter = LegacySubscriptionAdapter()
|
||||
private val channelsAdapter = SubscriptionChannelAdapter()
|
||||
|
||||
override fun setLayoutManagers(gridItems: Int) {
|
||||
_binding?.subFeed?.layoutManager = VideosAdapter.getLayout(requireContext(), gridItems)
|
||||
}
|
||||
@ -94,6 +96,27 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
|
||||
setupSortAndFilter()
|
||||
|
||||
binding.subFeed.adapter = feedAdapter
|
||||
|
||||
val legacySubscriptions = PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.LEGACY_SUBSCRIPTIONS,
|
||||
false
|
||||
)
|
||||
|
||||
if (legacySubscriptions) {
|
||||
binding.subChannels.layoutManager = GridLayoutManager(
|
||||
context,
|
||||
PreferenceHelper.getString(
|
||||
PreferenceKeys.LEGACY_SUBSCRIPTIONS_COLUMNS,
|
||||
"4"
|
||||
).toInt()
|
||||
)
|
||||
binding.subChannels.adapter = legacySubscriptionsAdapter
|
||||
} else {
|
||||
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||
binding.subChannels.adapter = channelsAdapter
|
||||
}
|
||||
|
||||
// Check if the AppBarLayout is fully expanded
|
||||
binding.subscriptionsAppBar.addOnOffsetChangedListener { _, verticalOffset ->
|
||||
isAppBarFullyExpanded = verticalOffset == 0
|
||||
@ -145,7 +168,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
|
||||
if (viewModel.subscriptions.value != null && isCurrentTabSubChannels) {
|
||||
binding.subRefresh.isRefreshing = true
|
||||
channelsAdapter?.updateItems()
|
||||
channelsAdapter.updateItems()
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
@ -204,8 +227,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
private fun loadNextFeedItems() {
|
||||
val binding = _binding ?: return
|
||||
|
||||
val feedAdapter = feedAdapter ?: return
|
||||
|
||||
val hasMore = sortedFeed.size > feedAdapter.itemCount
|
||||
if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels && !binding.subRefresh.isRefreshing && hasMore) {
|
||||
binding.subRefresh.isRefreshing = true
|
||||
@ -373,11 +394,8 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
val notLoaded = viewModel.videoFeed.value.isNullOrEmpty()
|
||||
binding.subFeed.isGone = notLoaded
|
||||
binding.emptyFeed.isVisible = notLoaded
|
||||
|
||||
feedAdapter = VideosAdapter(mutableListOf())
|
||||
loadNextFeedItems()
|
||||
|
||||
binding.subFeed.adapter = feedAdapter
|
||||
binding.toggleSubs.text = getString(R.string.subscriptions)
|
||||
|
||||
PreferenceHelper.updateLastFeedWatchedTime()
|
||||
@ -393,18 +411,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
)
|
||||
|
||||
if (legacySubscriptions) {
|
||||
binding.subChannels.layoutManager = GridLayoutManager(
|
||||
context,
|
||||
PreferenceHelper.getString(
|
||||
PreferenceKeys.LEGACY_SUBSCRIPTIONS_COLUMNS,
|
||||
"4"
|
||||
).toInt()
|
||||
)
|
||||
binding.subChannels.adapter = LegacySubscriptionAdapter(subscriptions)
|
||||
legacySubscriptionsAdapter.submitList(subscriptions)
|
||||
} else {
|
||||
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||
channelsAdapter = SubscriptionChannelAdapter(subscriptions.toMutableList())
|
||||
binding.subChannels.adapter = channelsAdapter
|
||||
channelsAdapter.submitList(subscriptions)
|
||||
}
|
||||
|
||||
binding.subRefresh.isRefreshing = false
|
||||
@ -420,7 +429,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
}
|
||||
|
||||
fun removeItem(videoId: String) {
|
||||
feedAdapter?.removeItemById(videoId)
|
||||
feedAdapter.removeItemById(videoId)
|
||||
sortedFeed.removeAll { it.url?.toID() != videoId }
|
||||
}
|
||||
|
||||
|
@ -30,15 +30,18 @@ class TrendsFragment : DynamicLayoutManagerFragment(R.layout.fragment_trends) {
|
||||
_binding = FragmentTrendsBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val adapter = VideosAdapter()
|
||||
binding.recview.adapter = adapter
|
||||
binding.recview.layoutManager?.onRestoreInstanceState(viewModel.recyclerViewState)
|
||||
|
||||
viewModel.trendingVideos.observe(viewLifecycleOwner) { videos ->
|
||||
if (videos == null) return@observe
|
||||
|
||||
binding.recview.adapter = VideosAdapter(videos.toMutableList())
|
||||
binding.recview.layoutManager?.onRestoreInstanceState(viewModel.recyclerViewState)
|
||||
|
||||
binding.homeRefresh.isRefreshing = false
|
||||
binding.progressBar.isGone = true
|
||||
|
||||
adapter.submitList(videos)
|
||||
|
||||
if (videos.isEmpty()) {
|
||||
Snackbar.make(binding.root, R.string.change_region, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.settings) {
|
||||
|
@ -50,6 +50,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
||||
private var isLoading = false
|
||||
private var recyclerViewState: Parcelable? = null
|
||||
|
||||
private val watchHistoryAdapter = WatchHistoryAdapter()
|
||||
|
||||
private var selectedStatusFilter = PreferenceHelper.getInt(
|
||||
PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER,
|
||||
0
|
||||
@ -80,6 +82,31 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
||||
_binding?.watchHistoryRecView?.updatePadding(bottom = if (it) 64f.dpToPx() else 0)
|
||||
}
|
||||
|
||||
binding.watchHistoryRecView.setOnDismissListener { position ->
|
||||
watchHistoryAdapter.removeFromWatchHistory(position)
|
||||
}
|
||||
|
||||
// observe changes to indicate if the history is empty
|
||||
watchHistoryAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
if (watchHistoryAdapter.itemCount == 0) {
|
||||
binding.historyContainer.isGone = true
|
||||
binding.historyEmpty.isVisible = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
binding.watchHistoryRecView.adapter = watchHistoryAdapter
|
||||
|
||||
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
|
||||
binding.watchHistoryRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
recyclerViewState = binding.watchHistoryRecView.layoutManager?.onSaveInstanceState()
|
||||
}
|
||||
})
|
||||
|
||||
lifecycleScope.launch {
|
||||
val history = withContext(Dispatchers.IO) {
|
||||
DatabaseHelper.getWatchHistoryPage(1, HISTORY_PAGE_SIZE)
|
||||
@ -139,14 +166,6 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
||||
}.show(childFragmentManager)
|
||||
}
|
||||
|
||||
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473
|
||||
binding.watchHistoryRecView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
recyclerViewState = binding.watchHistoryRecView.layoutManager?.onSaveInstanceState()
|
||||
}
|
||||
})
|
||||
|
||||
showWatchHistory(history)
|
||||
}
|
||||
|
||||
@ -157,7 +176,6 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
||||
|
||||
private fun showWatchHistory(history: List<WatchHistoryItem>) {
|
||||
val watchHistory = history.filterByStatusAndWatchPosition()
|
||||
val watchHistoryAdapter = WatchHistoryAdapter(watchHistory.toMutableList())
|
||||
|
||||
binding.playAll.setOnClickListener {
|
||||
PlayingQueue.resetToDefaults()
|
||||
@ -170,26 +188,10 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
||||
keepQueue = true
|
||||
)
|
||||
}
|
||||
|
||||
binding.watchHistoryRecView.adapter = watchHistoryAdapter
|
||||
watchHistoryAdapter.submitList(history)
|
||||
binding.historyEmpty.isGone = true
|
||||
binding.historyContainer.isVisible = true
|
||||
|
||||
binding.watchHistoryRecView.setOnDismissListener { position ->
|
||||
watchHistoryAdapter.removeFromWatchHistory(position)
|
||||
}
|
||||
|
||||
// observe changes to indicate if the history is empty
|
||||
watchHistoryAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
if (watchHistoryAdapter.itemCount == 0) {
|
||||
binding.historyContainer.isGone = true
|
||||
binding.historyEmpty.isVisible = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// add a listener for scroll end, delay needed to prevent loading new ones the first time
|
||||
handler.postDelayed(200) {
|
||||
if (_binding == null) return@postDelayed
|
||||
|
@ -187,8 +187,10 @@ class InstanceSettings : BasePreferenceFragment() {
|
||||
binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
val instances = ImmutableList.copyOf(this.instances)
|
||||
binding.optionsRecycler.adapter = InstancesAdapter(instances, selectedIndex) {
|
||||
binding.optionsRecycler.adapter = InstancesAdapter(selectedIndex) {
|
||||
selectedInstance = instances[it].apiUrl
|
||||
}.also {
|
||||
it.submitList(instances)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
|
@ -3,7 +3,6 @@ package com.github.libretube.ui.sheets
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.DialogAddChannelToGroupBinding
|
||||
@ -16,6 +15,10 @@ import kotlinx.coroutines.withContext
|
||||
class AddChannelToGroupSheet : ExpandedBottomSheet(R.layout.dialog_add_channel_to_group) {
|
||||
private lateinit var channelId: String
|
||||
|
||||
private val addToGroupAdapter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
AddChannelToGroupAdapter(channelId)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -26,7 +29,8 @@ class AddChannelToGroupSheet : ExpandedBottomSheet(R.layout.dialog_add_channel_t
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val binding = DialogAddChannelToGroupBinding.bind(view)
|
||||
|
||||
binding.groupsRV.layoutManager = LinearLayoutManager(context)
|
||||
binding.groupsRV.adapter = addToGroupAdapter
|
||||
|
||||
binding.cancel.setOnClickListener {
|
||||
requireDialog().dismiss()
|
||||
}
|
||||
@ -36,7 +40,7 @@ class AddChannelToGroupSheet : ExpandedBottomSheet(R.layout.dialog_add_channel_t
|
||||
val subscriptionGroups = subGroupsDao.getAll().sortedBy { it.index }.toMutableList()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.groupsRV.adapter = AddChannelToGroupAdapter(subscriptionGroups, channelId)
|
||||
addToGroupAdapter.submitList(subscriptionGroups)
|
||||
|
||||
binding.okay.setOnClickListener {
|
||||
requireDialog().hide()
|
||||
|
@ -30,8 +30,16 @@ class EditChannelGroupSheet : ExpandedBottomSheet(R.layout.dialog_edit_channel_g
|
||||
private val channelGroupsModel: EditChannelGroupsModel by activityViewModels()
|
||||
private var channels = listOf<Subscription>()
|
||||
|
||||
private val channelsAdapter = SubscriptionGroupChannelsAdapter(
|
||||
channelGroupsModel.groupToEdit!!
|
||||
) {
|
||||
channelGroupsModel.groupToEdit = it
|
||||
updateConfirmStatus()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = DialogEditChannelGroupBinding.bind(view)
|
||||
binding.channelsRV.adapter = channelsAdapter
|
||||
binding.groupName.setText(channelGroupsModel.groupToEdit?.name)
|
||||
val oldGroupName = channelGroupsModel.groupToEdit?.name.orEmpty()
|
||||
|
||||
@ -97,15 +105,12 @@ class EditChannelGroupSheet : ExpandedBottomSheet(R.layout.dialog_edit_channel_g
|
||||
}
|
||||
|
||||
private fun showChannels(channels: List<Subscription>, query: String?) {
|
||||
binding.channelsRV.adapter = SubscriptionGroupChannelsAdapter(
|
||||
channels.filter { query == null || it.name.lowercase().contains(query.lowercase()) },
|
||||
channelGroupsModel.groupToEdit!!
|
||||
) {
|
||||
channelGroupsModel.groupToEdit = it
|
||||
updateConfirmStatus()
|
||||
}
|
||||
binding.subscriptionsContainer.isVisible = true
|
||||
binding.progress.isVisible = false
|
||||
|
||||
channelsAdapter.submitList(
|
||||
channels.filter { query == null || it.name.lowercase().contains(query.lowercase()) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateConfirmStatus() {
|
||||
|
@ -10,7 +10,6 @@ import androidx.core.text.method.LinkMovementMethodCompat
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Segment
|
||||
import com.github.libretube.api.obj.Streams
|
||||
@ -33,6 +32,8 @@ class DescriptionLayout(
|
||||
private var streams: Streams? = null
|
||||
var handleLink: (link: String) -> Unit = {}
|
||||
|
||||
private val videoTagsAdapter = VideoTagsAdapter()
|
||||
|
||||
init {
|
||||
binding.playerTitleLayout.setOnClickListener {
|
||||
toggleDescription()
|
||||
@ -41,6 +42,8 @@ class DescriptionLayout(
|
||||
streams?.title?.let { ClipboardHelper.save(context, text = it) }
|
||||
true
|
||||
}
|
||||
|
||||
binding.tagsRecycler.adapter = videoTagsAdapter
|
||||
}
|
||||
|
||||
fun setSegments(segments: List<Segment>) {
|
||||
@ -101,9 +104,7 @@ class DescriptionLayout(
|
||||
"${context?.getString(R.string.visibility)}: $visibility"
|
||||
|
||||
if (streams.tags.isNotEmpty()) {
|
||||
binding.tagsRecycler.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.tagsRecycler.adapter = VideoTagsAdapter(streams.tags)
|
||||
videoTagsAdapter.submitList(streams.tags)
|
||||
}
|
||||
binding.tagsRecycler.isVisible = streams.tags.isNotEmpty()
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="false"
|
||||
android:visibility="gone"
|
||||
android:text="Sponsor"/>
|
||||
android:text="Sponsor"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -133,7 +133,9 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tags_recycler"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -48,7 +49,9 @@
|
||||
android:id="@+id/groupsRV"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
android:layout_weight="1"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/add_channel_to_group_row" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -35,10 +36,12 @@
|
||||
android:id="@+id/featuredRV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/watchingTV"
|
||||
@ -49,10 +52,12 @@
|
||||
android:id="@+id/watchingRV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trendingTV"
|
||||
@ -70,7 +75,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@ -84,7 +91,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlistsTV"
|
||||
@ -96,7 +105,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -134,8 +144,8 @@
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="12sp"
|
||||
android:text="@string/retry"/>
|
||||
android:text="@string/retry"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/change_instance"
|
||||
@ -144,8 +154,8 @@
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="5dp"
|
||||
android:textSize="12sp"
|
||||
android:text="@string/change_instance"/>
|
||||
android:text="@string/change_instance"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -9,7 +10,10 @@
|
||||
android:id="@+id/suggestions_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="10dp" />
|
||||
android:layout_marginVertical="10dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:reverseLayout="true"
|
||||
app:stackFromEnd="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/history_empty"
|
||||
|
Loading…
x
Reference in New Issue
Block a user