Merge pull request #1691 from Bnyro/master

alternative global layout
This commit is contained in:
Bnyro 2022-10-29 17:34:22 +02:00 committed by GitHub
commit 5e546eca44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 231 additions and 250 deletions

View File

@ -33,7 +33,7 @@ object PreferenceKeys {
const val APP_ICON = "icon_change"
const val LEGACY_SUBSCRIPTIONS = "legacy_subscriptions"
const val LEGACY_SUBSCRIPTIONS_COLUMNS = "legacy_subscriptions_columns"
const val ALTERNATIVE_TRENDING_LAYOUT = "trending_layout"
const val ALTERNATIVE_VIDEOS_LAYOUT = "alternative_videos_layout"
const val NEW_VIDEOS_BADGE = "new_videos_badge"
const val PLAYLISTS_ORDER = "playlists_order"

View File

@ -1,92 +0,0 @@
package com.github.libretube.ui.adapters
import android.annotation.SuppressLint
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.setWatchProgressLength
import com.github.libretube.extensions.toID
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.ChannelViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class ChannelAdapter(
private val videoFeed: MutableList<StreamItem>,
private val childFragmentManager: FragmentManager,
private val showChannelInfo: Boolean = false
) :
RecyclerView.Adapter<ChannelViewHolder>() {
override fun getItemCount(): Int {
return videoFeed.size
}
fun updateItems(newItems: List<StreamItem>) {
val feedSize = videoFeed.size
videoFeed.addAll(newItems)
notifyItemRangeInserted(feedSize, newItems.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
return ChannelViewHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
val video = videoFeed[position]
// hide the item if there was an extractor error
if (video.title == null) {
holder.itemView.visibility = View.GONE
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
return
}
holder.binding.apply {
videoTitle.text = video.title
videoInfo.text =
video.views.formatShort() + " " +
root.context.getString(R.string.views_placeholder) +
"" + video.uploaded?.let { DateUtils.getRelativeTimeSpanString(it) }
thumbnailDuration.text =
video.duration?.let { DateUtils.formatElapsedTime(it) }
ImageHelper.loadImage(video.thumbnail, thumbnail)
if (showChannelInfo) {
ImageHelper.loadImage(video.uploaderAvatar, channelImage)
channelName.text = video.uploaderName
}
root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, video.url)
}
val videoId = video.url?.toID()
val videoName = video.title
root.setOnLongClickListener {
if (videoId == null || videoName == null) return@setOnLongClickListener true
VideoOptionsBottomSheet(videoId, videoName)
.show(childFragmentManager, VideoOptionsBottomSheet::class.java.name)
true
}
if (videoId != null) {
watchProgress.setWatchProgressLength(videoId, video.duration ?: 0L)
}
}
}
}

View File

@ -1,93 +0,0 @@
package com.github.libretube.ui.adapters
import android.annotation.SuppressLint
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.databinding.TrendingRowBinding
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.setFormattedDuration
import com.github.libretube.extensions.setWatchProgressLength
import com.github.libretube.extensions.toID
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.SubscriptionViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class TrendingAdapter(
private val streamItems: List<com.github.libretube.api.obj.StreamItem>,
private val childFragmentManager: FragmentManager,
private val showAllAtOne: Boolean = true
) : RecyclerView.Adapter<SubscriptionViewHolder>() {
var index = 10
override fun getItemCount(): Int {
return if (showAllAtOne) {
streamItems.size
} else if (index >= streamItems.size) {
streamItems.size - 1
} else {
index
}
}
fun updateItems() {
val oldSize = index
index += 10
notifyItemRangeInserted(oldSize, index)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
return SubscriptionViewHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
val video = streamItems[position]
// hide the item if there was an extractor error
if (video.title == null) {
holder.itemView.visibility = View.GONE
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
return
}
holder.binding.apply {
textViewTitle.text = video.title
textViewChannel.text =
video.uploaderName + "" +
video.views.formatShort() + " " +
root.context.getString(R.string.views_placeholder) +
"" + video.uploaded?.let { DateUtils.getRelativeTimeSpanString(it) }
video.duration?.let { thumbnailDuration.setFormattedDuration(it) }
channelImage.setOnClickListener {
NavigationHelper.navigateChannel(root.context, video.uploaderUrl)
}
ImageHelper.loadImage(video.thumbnail, thumbnail)
ImageHelper.loadImage(video.uploaderAvatar, channelImage)
root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, video.url)
}
val videoId = video.url?.toID()
val videoName = video.title
root.setOnLongClickListener {
if (videoId == null || videoName == null) return@setOnLongClickListener true
VideoOptionsBottomSheet(videoId, videoName)
.show(childFragmentManager, VideoOptionsBottomSheet::class.java.name)
true
}
if (videoId != null) {
watchProgress.setWatchProgressLength(videoId, video.duration ?: 0L)
}
}
}
}

View File

@ -0,0 +1,155 @@
package com.github.libretube.ui.adapters
import android.annotation.SuppressLint
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.TrendingRowBinding
import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.setFormattedDuration
import com.github.libretube.extensions.setWatchProgressLength
import com.github.libretube.extensions.toID
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.VideosViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.PreferenceHelper
class VideosAdapter(
private val streamItems: MutableList<StreamItem>,
private val childFragmentManager: FragmentManager,
private val showAllAtOnce: Boolean = true,
private val forceType: Int = FORCE_NONE
) : RecyclerView.Adapter<VideosViewHolder>() {
var index = 10
override fun getItemCount(): Int {
return when {
showAllAtOnce -> streamItems.size
index >= streamItems.size -> streamItems.size - 1
else -> index
}
}
fun updateItems() {
val oldSize = index
index += 10
notifyItemRangeInserted(oldSize, index)
}
fun insertItems(newItems: List<StreamItem>) {
val feedSize = streamItems.size
streamItems.addAll(newItems)
notifyItemRangeInserted(feedSize, newItems.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideosViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when {
forceType == FORCE_TRENDING -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
forceType == FORCE_CHANNEL -> VideosViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
PreferenceHelper.getBoolean(
PreferenceKeys.ALTERNATIVE_VIDEOS_LAYOUT,
false
) -> VideosViewHolder(VideoRowBinding.inflate(layoutInflater, parent, false))
else -> VideosViewHolder(TrendingRowBinding.inflate(layoutInflater, parent, false))
}
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: VideosViewHolder, position: Int) {
val video = streamItems[position]
// hide the item if there was an extractor error
if (video.title == null) {
holder.itemView.visibility = View.GONE
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
return
}
// Trending layout
holder.trendingRowBinding?.apply {
textViewTitle.text = video.title
textViewChannel.text =
video.uploaderName + "" +
video.views.formatShort() + " " +
root.context.getString(R.string.views_placeholder) +
"" + video.uploaded?.let { DateUtils.getRelativeTimeSpanString(it) }
video.duration?.let { thumbnailDuration.setFormattedDuration(it) }
channelImage.setOnClickListener {
NavigationHelper.navigateChannel(root.context, video.uploaderUrl)
}
ImageHelper.loadImage(video.thumbnail, thumbnail)
ImageHelper.loadImage(video.uploaderAvatar, channelImage)
root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, video.url)
}
val videoId = video.url?.toID()
val videoName = video.title
root.setOnLongClickListener {
if (videoId == null || videoName == null) return@setOnLongClickListener true
VideoOptionsBottomSheet(videoId, videoName)
.show(childFragmentManager, VideoOptionsBottomSheet::class.java.name)
true
}
if (videoId != null) {
watchProgress.setWatchProgressLength(videoId, video.duration ?: 0L)
}
}
// Normal videos row layout
holder.videoRowBinding?.apply {
videoTitle.text = video.title
videoInfo.text =
video.views.formatShort() + " " +
root.context.getString(R.string.views_placeholder) +
"" + video.uploaded?.let { DateUtils.getRelativeTimeSpanString(it) }
thumbnailDuration.text =
video.duration?.let { DateUtils.formatElapsedTime(it) }
ImageHelper.loadImage(video.thumbnail, thumbnail)
if (forceType != FORCE_CHANNEL) {
ImageHelper.loadImage(video.uploaderAvatar, channelImage)
channelName.text = video.uploaderName
}
root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, video.url)
}
val videoId = video.url?.toID()
val videoName = video.title
root.setOnLongClickListener {
if (videoId == null || videoName == null) return@setOnLongClickListener true
VideoOptionsBottomSheet(videoId, videoName)
.show(childFragmentManager, VideoOptionsBottomSheet::class.java.name)
true
}
if (videoId != null) {
watchProgress.setWatchProgressLength(videoId, video.duration ?: 0L)
}
}
}
companion object {
const val FORCE_NONE = 0
const val FORCE_TRENDING = 1
const val FORCE_NORMAL = 2
const val FORCE_CHANNEL = 3
}
}

View File

@ -20,8 +20,8 @@ import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID
import com.github.libretube.obj.ShareData
import com.github.libretube.ui.adapters.ChannelAdapter
import com.github.libretube.ui.adapters.SearchAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.util.ImageHelper
@ -38,7 +38,7 @@ class ChannelFragment : BaseFragment() {
private var channelId: String? = null
private var channelName: String? = null
private var nextPage: String? = null
private var channelAdapter: ChannelAdapter? = null
private var channelAdapter: VideosAdapter? = null
private var isLoading = true
private var isSubscribed: Boolean? = false
@ -184,9 +184,10 @@ class ChannelFragment : BaseFragment() {
ImageHelper.loadImage(response.avatarUrl, binding.channelImage)
// recyclerview of the videos by the channel
channelAdapter = ChannelAdapter(
channelAdapter = VideosAdapter(
response.relatedStreams.orEmpty().toMutableList(),
childFragmentManager
childFragmentManager,
forceType = VideosAdapter.FORCE_CHANNEL
)
binding.channelRecView.adapter = channelAdapter
}
@ -272,7 +273,7 @@ class ChannelFragment : BaseFragment() {
return@launchWhenCreated
}
nextPage = response.nextpage
channelAdapter?.updateItems(response.relatedStreams!!)
channelAdapter?.insertItems(response.relatedStreams.orEmpty())
isLoading = false
binding.channelRefresh.isRefreshing = false
}

View File

@ -8,17 +8,15 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.extensions.TAG
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.ChannelAdapter
import com.github.libretube.ui.adapters.TrendingAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.util.LayoutHelper
import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.snackbar.Snackbar
@ -104,30 +102,12 @@ class HomeFragment : BaseFragment() {
return@runOnUiThread
}
if (
PreferenceHelper.getBoolean(
PreferenceKeys.ALTERNATIVE_TRENDING_LAYOUT,
false
)
) {
binding.recview.layoutManager = LinearLayoutManager(context)
binding.recview.adapter = VideosAdapter(
response.toMutableList(),
childFragmentManager
)
binding.recview.adapter = ChannelAdapter(
response.toMutableList(),
childFragmentManager,
true
)
} else {
binding.recview.layoutManager = GridLayoutManager(
context,
PreferenceHelper.getString(
PreferenceKeys.GRID_COLUMNS,
resources.getInteger(R.integer.grid_items).toString()
).toInt()
)
binding.recview.adapter = TrendingAdapter(response, childFragmentManager)
}
binding.recview.layoutManager = LayoutHelper.getVideoLayout(requireContext())
}
}
}

View File

@ -63,7 +63,7 @@ import com.github.libretube.services.DownloadService
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.adapters.ChaptersAdapter
import com.github.libretube.ui.adapters.CommentsAdapter
import com.github.libretube.ui.adapters.TrendingAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
import com.github.libretube.ui.dialogs.DownloadDialog
@ -903,9 +903,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
}
if (PlayerHelper.relatedStreamsEnabled) {
// only show related streams if enabled
binding.relatedRecView.adapter = TrendingAdapter(
response.relatedStreams!!,
childFragmentManager
binding.relatedRecView.adapter = VideosAdapter(
response.relatedStreams.orEmpty().toMutableList(),
childFragmentManager,
forceType = VideosAdapter.FORCE_TRENDING
)
}
// set video description

View File

@ -15,16 +15,17 @@ import com.github.libretube.databinding.FragmentSubscriptionsBinding
import com.github.libretube.models.SubscriptionsViewModel
import com.github.libretube.ui.adapters.LegacySubscriptionAdapter
import com.github.libretube.ui.adapters.SubscriptionChannelAdapter
import com.github.libretube.ui.adapters.TrendingAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.views.BottomSheet
import com.github.libretube.util.LayoutHelper
import com.github.libretube.util.PreferenceHelper
class SubscriptionsFragment : BaseFragment() {
private lateinit var binding: FragmentSubscriptionsBinding
private val viewModel: SubscriptionsViewModel by activityViewModels()
private var subscriptionAdapter: TrendingAdapter? = null
private var subscriptionAdapter: VideosAdapter? = null
private var sortOrder = 0
override fun onCreateView(
@ -48,12 +49,7 @@ class SubscriptionsFragment : BaseFragment() {
binding.subProgress.visibility = View.VISIBLE
val grid = PreferenceHelper.getString(
PreferenceKeys.GRID_COLUMNS,
resources.getInteger(R.integer.grid_items).toString()
)
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
binding.subFeed.layoutManager = LayoutHelper.getVideoLayout(requireContext())
if (viewModel.videoFeed.value == null || !loadFeedInBackground) {
viewModel.videoFeed.value = null
@ -158,7 +154,11 @@ class SubscriptionsFragment : BaseFragment() {
if (viewModel.videoFeed.value!!.isEmpty()) View.VISIBLE else View.GONE
binding.subProgress.visibility = View.GONE
subscriptionAdapter = TrendingAdapter(sortedFeed, childFragmentManager, false)
subscriptionAdapter = VideosAdapter(
sortedFeed.toMutableList(),
childFragmentManager,
showAllAtOnce = false
)
binding.subFeed.adapter = subscriptionAdapter
}

View File

@ -1,8 +0,0 @@
package com.github.libretube.ui.viewholders
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.VideoRowBinding
class ChannelViewHolder(
val binding: VideoRowBinding
) : RecyclerView.ViewHolder(binding.root)

View File

@ -1,8 +0,0 @@
package com.github.libretube.ui.viewholders
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.TrendingRowBinding
class SubscriptionViewHolder(
val binding: TrendingRowBinding
) : RecyclerView.ViewHolder(binding.root)

View File

@ -0,0 +1,18 @@
package com.github.libretube.ui.viewholders
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.TrendingRowBinding
import com.github.libretube.databinding.VideoRowBinding
class VideosViewHolder : RecyclerView.ViewHolder {
var trendingRowBinding: TrendingRowBinding? = null
var videoRowBinding: VideoRowBinding? = null
constructor(binding: TrendingRowBinding) : super(binding.root) {
trendingRowBinding = binding
}
constructor(binding: VideoRowBinding) : super(binding.root) {
videoRowBinding = binding
}
}

View File

@ -0,0 +1,26 @@
package com.github.libretube.util
import android.content.Context
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys
object LayoutHelper {
fun getVideoLayout(context: Context) = if (
PreferenceHelper.getBoolean(
PreferenceKeys.ALTERNATIVE_VIDEOS_LAYOUT,
false
)
) {
LinearLayoutManager(context)
} else {
GridLayoutManager(
context,
PreferenceHelper.getString(
PreferenceKeys.GRID_COLUMNS,
context.resources.getInteger(R.integer.grid_items).toString()
).toInt()
)
}
}

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground">
android:background="@drawable/rounded_ripple">
<androidx.cardview.widget.CardView
android:id="@+id/thumbnailcard"

View File

@ -353,6 +353,7 @@
<string name="sb_markers">Markers</string>
<string name="sb_markers_summary">Mark the segments on the time bar.</string>
<string name="livestreams">Livestreams</string>
<string name="alternative_videos_layout">Alternative videos layout</string>
<!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string>

View File

@ -103,8 +103,8 @@
<SwitchPreferenceCompat
android:icon="@drawable/ic_trending"
app:defaultValue="false"
app:key="trending_layout"
app:title="@string/trending_layout" />
app:key="alternative_videos_layout"
app:title="@string/alternative_videos_layout" />
<ListPreference
android:defaultValue="recent"