Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Rewrite search functionality using Paging #5528

Merged
merged 4 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,7 @@
/* Baseline profile generation */
implementation(libs.androidx.profileinstaller)
"baselineProfile"(project(":baselineprofile"))

/* AndroidX Paging */

Check failure on line 145 in app/build.gradle.kts

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Replace the block comment with an EOL comment Raw Output: app/build.gradle.kts:145:5: error: Replace the block comment with an EOL comment (standard:no-single-line-block-comment)
implementation(libs.androidx.paging)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.github.libretube.ui.adapters

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.recyclerview.widget.ListAdapter
import com.github.libretube.R
import com.github.libretube.api.JsonHelper
import com.github.libretube.api.obj.ContentItem
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.ChannelRowBinding
import com.github.libretube.databinding.PlaylistsRowBinding
import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.adapters.callbacks.SearchCallback
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength
import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.SearchViewHolder
import com.github.libretube.util.TextUtils
import kotlinx.serialization.encodeToString

// TODO: Replace with SearchResultsAdapter when migrating the channel fragment to use Paging as well
class SearchChannelAdapter : ListAdapter<ContentItem, SearchViewHolder>(SearchCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {

Check failure on line 35 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Newline expected after opening parenthesis Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:35:37: error: Newline expected after opening parenthesis (standard:function-signature)

Check failure on line 35 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Parameter should start on a newline Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:35:56: error: Parameter should start on a newline (standard:function-signature)

Check failure on line 35 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Newline expected before closing parenthesis Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:35:69: error: Newline expected before closing parenthesis (standard:function-signature)
val layoutInflater = LayoutInflater.from(parent.context)

return when (viewType) {
0 -> SearchViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
1 -> SearchViewHolder(ChannelRowBinding.inflate(layoutInflater, parent, false))
2 -> SearchViewHolder(PlaylistsRowBinding.inflate(layoutInflater, parent, false))
else -> throw IllegalArgumentException("Invalid type")
}
}

override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {

Check failure on line 46 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Newline expected after opening parenthesis Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:46:35: error: Newline expected after opening parenthesis (standard:function-signature)

Check failure on line 46 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Parameter should start on a newline Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:46:61: error: Parameter should start on a newline (standard:function-signature)

Check failure on line 46 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Newline expected before closing parenthesis Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:46:74: error: Newline expected before closing parenthesis (standard:function-signature)
val searchItem = currentList[position]

val videoRowBinding = holder.videoRowBinding
val channelRowBinding = holder.channelRowBinding
val playlistRowBinding = holder.playlistRowBinding

if (videoRowBinding != null) {
bindVideo(searchItem, videoRowBinding, position)
} else if (channelRowBinding != null) {
bindChannel(searchItem, channelRowBinding)
} else if (playlistRowBinding != null) {
bindPlaylist(searchItem, playlistRowBinding)
}
}

override fun getItemViewType(position: Int): Int {
return when (currentList[position].type) {
StreamItem.TYPE_STREAM -> 0
StreamItem.TYPE_CHANNEL -> 1
StreamItem.TYPE_PLAYLIST -> 2
else -> 3
}
}

private fun bindVideo(item: ContentItem, binding: VideoRowBinding, position: Int) {

Check failure on line 71 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Newline expected after opening parenthesis Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:71:27: error: Newline expected after opening parenthesis (standard:function-signature)

Check failure on line 71 in app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Parameter should start on a newline Raw Output: app/src/main/java/com/github/libretube/ui/adapters/SearchChannelAdapter.kt:71:46: error: Parameter should start on a newline (standard:function-signature)
binding.apply {
Isira-Seneviratne marked this conversation as resolved.
Show resolved Hide resolved
thumbnail.setImageDrawable(null)
ImageHelper.loadImage(item.thumbnail, thumbnail)
thumbnailDuration.setFormattedDuration(item.duration, item.isShort)
videoTitle.text = item.title

val viewsString = item.views.takeIf { it != -1L }?.formatShort().orEmpty()
val uploadDate = item.uploaded.takeIf { it > 0 }?.let {
" ${TextUtils.SEPARATOR} ${TextUtils.formatRelativeDate(root.context, it)}"
}.orEmpty()
videoInfo.text = root.context.getString(
R.string.normal_views,
viewsString,
uploadDate
)

channelContainer.isGone = true

root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, item.url)
}

val videoId = item.url.toID()
val activity = (root.context as BaseActivity)
val fragmentManager = activity.supportFragmentManager
root.setOnLongClickListener {
fragmentManager.setFragmentResultListener(
VideoOptionsBottomSheet.VIDEO_OPTIONS_SHEET_REQUEST_KEY,
activity
) { _, _ ->
notifyItemChanged(position)
}
val sheet = VideoOptionsBottomSheet()
val contentItemString = JsonHelper.json.encodeToString(item)
val streamItem: StreamItem = JsonHelper.json.decodeFromString(contentItemString)
sheet.arguments = bundleOf(IntentData.streamItem to streamItem)
sheet.show(fragmentManager, SearchChannelAdapter::class.java.name)
true
}
channelContainer.setOnClickListener {
NavigationHelper.navigateChannel(root.context, item.uploaderUrl)
}
watchProgress.setWatchProgressLength(videoId, item.duration)
}
}

private fun bindChannel(item: ContentItem, binding: ChannelRowBinding) {
binding.apply {
searchChannelImage.setImageDrawable(null)
ImageHelper.loadImage(item.thumbnail, searchChannelImage, true)
searchChannelName.text = item.name

val subscribers = item.subscribers.formatShort()
searchViews.text = if (item.subscribers >= 0 && item.videos >= 0) {
root.context.getString(R.string.subscriberAndVideoCounts, subscribers, item.videos)
} else if (item.subscribers >= 0) {
root.context.getString(R.string.subscribers, subscribers)
} else if (item.videos >= 0) {
root.context.getString(R.string.videoCount, item.videos)
} else {
""
}

root.setOnClickListener {
NavigationHelper.navigateChannel(root.context, item.url)
}

var subscribed = false
binding.searchSubButton.setupSubscriptionButton(item.url.toID(), item.name?.toID()) {
subscribed = it
}

root.setOnLongClickListener {
val channelOptionsSheet = ChannelOptionsBottomSheet()
channelOptionsSheet.arguments = bundleOf(
IntentData.channelId to item.url.toID(),
IntentData.channelName to item.name,
IntentData.isSubscribed to subscribed
)
channelOptionsSheet.show((root.context as BaseActivity).supportFragmentManager)
true
}
}
}

private fun bindPlaylist(item: ContentItem, binding: PlaylistsRowBinding) {
binding.apply {
playlistThumbnail.setImageDrawable(null)
ImageHelper.loadImage(item.thumbnail, playlistThumbnail)
if (item.videos != -1L) videoCount.text = item.videos.toString()
playlistTitle.text = item.name
playlistDescription.text = item.uploaderName
root.setOnClickListener {
NavigationHelper.navigatePlaylist(root.context, item.url, PlaylistType.PUBLIC)
}

root.setOnLongClickListener {
val playlistId = item.url.toID()
val playlistName = item.name!!
val sheet = PlaylistOptionsBottomSheet()
sheet.arguments = bundleOf(
IntentData.playlistId to playlistId,
IntentData.playlistName to playlistName,
IntentData.playlistType to PlaylistType.PUBLIC
)
sheet.show(
(root.context as BaseActivity).supportFragmentManager,
PlaylistOptionsBottomSheet::class.java.name
)
true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package com.github.libretube.ui.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.paging.PagingDataAdapter
import com.github.libretube.R
import com.github.libretube.api.JsonHelper
import com.github.libretube.api.obj.ContentItem
Expand All @@ -19,6 +17,7 @@ import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.adapters.callbacks.SearchCallback
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength
Expand All @@ -30,10 +29,9 @@ import com.github.libretube.ui.viewholders.SearchViewHolder
import com.github.libretube.util.TextUtils
import kotlinx.serialization.encodeToString

class SearchAdapter(
private val isChannelAdapter: Boolean = false,
class SearchResultsAdapter(
private val timeStamp: Long = 0
) : ListAdapter<ContentItem, SearchViewHolder>(SearchCallback) {
) : PagingDataAdapter<ContentItem, SearchViewHolder>(SearchCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)

Expand All @@ -55,7 +53,7 @@ class SearchAdapter(
}

override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
val searchItem = currentList[position]
val searchItem = getItem(position)!!

val videoRowBinding = holder.videoRowBinding
val channelRowBinding = holder.channelRowBinding
Expand All @@ -71,7 +69,7 @@ class SearchAdapter(
}

override fun getItemViewType(position: Int): Int {
return when (currentList[position].type) {
return when (getItem(position)?.type) {
StreamItem.TYPE_STREAM -> 0
StreamItem.TYPE_CHANNEL -> 1
StreamItem.TYPE_PLAYLIST -> 2
Expand All @@ -95,13 +93,8 @@ class SearchAdapter(
uploadDate
)

// only display channel related info if not in a channel tab
if (!isChannelAdapter) {
channelName.text = item.uploaderName
ImageHelper.loadImage(item.uploaderAvatar, channelImage, true)
} else {
channelContainer.isGone = true
}
channelName.text = item.uploaderName
ImageHelper.loadImage(item.uploaderAvatar, channelImage, true)

root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, item.url, timestamp = timeStamp)
Expand All @@ -121,7 +114,7 @@ class SearchAdapter(
val contentItemString = JsonHelper.json.encodeToString(item)
val streamItem: StreamItem = JsonHelper.json.decodeFromString(contentItemString)
sheet.arguments = bundleOf(IntentData.streamItem to streamItem)
sheet.show(fragmentManager, SearchAdapter::class.java.name)
sheet.show(fragmentManager, SearchResultsAdapter::class.java.name)
true
}
channelContainer.setOnClickListener {
Expand Down Expand Up @@ -196,14 +189,4 @@ class SearchAdapter(
}
}
}

private object SearchCallback : DiffUtil.ItemCallback<ContentItem>() {
override fun areItemsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean {
return oldItem.url == newItem.url
}

override fun areContentsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean {
return true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.libretube.ui.adapters.callbacks

import androidx.recyclerview.widget.DiffUtil
import com.github.libretube.api.obj.ContentItem

object SearchCallback : DiffUtil.ItemCallback<ContentItem>() {
override fun areItemsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean {
return oldItem.url == newItem.url
}

override fun areContentsTheSame(oldItem: ContentItem, newItem: ContentItem) = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.obj.ChannelTabs
import com.github.libretube.obj.ShareData
import com.github.libretube.ui.adapters.SearchAdapter
import com.github.libretube.ui.adapters.SearchChannelAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.dialogs.ShareDialog
Expand Down Expand Up @@ -57,7 +57,7 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
)
private var channelTabs: List<ChannelTab> = emptyList()
private var nextPages = Array<String?>(5) { null }
private var searchAdapter: SearchAdapter? = null
private var searchChannelAdapter: SearchChannelAdapter? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -293,9 +293,9 @@ class ChannelFragment : DynamicLayoutManagerFragment() {

val binding = _binding ?: return@launch

searchAdapter = SearchAdapter(true)
binding.channelRecView.adapter = searchAdapter
searchAdapter?.submitList(response.content)
searchChannelAdapter = SearchChannelAdapter()
binding.channelRecView.adapter = searchChannelAdapter
searchChannelAdapter?.submitList(response.content)

binding.channelRefresh.isRefreshing = false
isLoading = false
Expand All @@ -320,7 +320,7 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
content = content.deArrow()
}

searchAdapter?.let {
searchChannelAdapter?.let {
it.submitList(it.currentList + newContent.content)
}

Expand Down
Loading
Loading