Merge pull request #3648 from Bnyro/master

Sort menu for local and private playlist
This commit is contained in:
Bnyro 2023-04-27 20:12:53 +02:00 committed by GitHub
commit 9cb2cfd6aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 60 deletions

View File

@ -38,6 +38,7 @@ object PreferenceKeys {
const val ALTERNATIVE_VIDEOS_LAYOUT = "alternative_videos_layout" const val ALTERNATIVE_VIDEOS_LAYOUT = "alternative_videos_layout"
const val NEW_VIDEOS_BADGE = "new_videos_badge" const val NEW_VIDEOS_BADGE = "new_videos_badge"
const val PLAYLISTS_ORDER = "playlists_order" const val PLAYLISTS_ORDER = "playlists_order"
const val PLAYLIST_SORT_ORDER = "playlist_sort_order"
/** /**
* Instance * Instance

View File

@ -6,6 +6,7 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -22,6 +23,7 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Playlist import com.github.libretube.api.obj.Playlist
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentPlaylistBinding import com.github.libretube.databinding.FragmentPlaylistBinding
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
@ -31,8 +33,10 @@ import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.PlaylistAdapter import com.github.libretube.ui.adapters.PlaylistAdapter
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -58,6 +62,11 @@ class PlaylistFragment : Fragment() {
// view models // view models
private val playerViewModel: PlayerViewModel by activityViewModels() private val playerViewModel: PlayerViewModel by activityViewModels()
private var selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.PLAYLIST_SORT_ORDER, 0)
set(value) {
PreferenceHelper.putInt(PreferenceKeys.PLAYLIST_SORT_ORDER, value)
field = value
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -139,6 +148,8 @@ class PlaylistFragment : Fragment() {
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos) binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
showPlaylistVideos(response)
// show playlist options // show playlist options
binding.optionsMenu.setOnClickListener { binding.optionsMenu.setOnClickListener {
PlaylistOptionsBottomSheet( PlaylistOptionsBottomSheet(
@ -197,67 +208,17 @@ class PlaylistFragment : Fragment() {
keepQueue = true keepQueue = true
) )
} }
} binding.sortMenu.isGone = false
binding.sortMenu.setOnClickListener {
val sortOptions = resources.getStringArray(R.array.playlistSortOptions)
playlistAdapter = PlaylistAdapter(playlistFeed, playlistId!!, playlistType) BaseBottomSheet().apply {
setSimpleItems(sortOptions.toList()) { index ->
// listen for playlist items to become deleted selectedSortOrder = index
playlistAdapter!!.registerAdapterDataObserver(object : showPlaylistVideos(response)
RecyclerView.AdapterDataObserver() { }
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { }.show(childFragmentManager)
if (positionStart == 0) {
ImageHelper.loadImage(
playlistFeed.firstOrNull()?.thumbnail ?: "",
binding.thumbnail
)
}
binding.playlistInfo.text =
getChannelAndVideoString(response, playlistFeed.size)
} }
})
binding.playlistRecView.adapter = playlistAdapter
binding.playlistScrollview.viewTreeObserver.addOnScrollChangedListener {
if (_binding?.playlistScrollview?.canScrollVertically(1) == false &&
!isLoading
) {
// append more playlists to the recycler view
if (playlistType != PlaylistType.PUBLIC) {
isLoading = true
playlistAdapter?.showMoreItems()
isLoading = false
} else {
fetchNextPage()
}
}
}
// listener for swiping to the left or right
if (playlistType != PlaylistType.PUBLIC) {
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(
viewHolder: RecyclerView.ViewHolder,
direction: Int
) {
val position = viewHolder.absoluteAdapterPosition
playlistAdapter!!.removeFromPlaylist(requireContext(), position)
}
}
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
} }
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -275,6 +236,88 @@ class PlaylistFragment : Fragment() {
} }
} }
private fun showPlaylistVideos(playlist: Playlist) {
val videos = if (playlistType == PlaylistType.PUBLIC) playlistFeed
else {
when (selectedSortOrder) {
0, 1 -> {
if (playlistType == PlaylistType.LOCAL) playlistFeed.sortedBy {
it.url.orEmpty().toInt()
} else playlistFeed
}
2, 3 -> {
playlistFeed.sortedBy { it.duration }
}
4, 5 -> {
playlistFeed.sortedBy { it.title }
}
else -> throw IllegalArgumentException()
}.let {
if (selectedSortOrder % 2 == 0) it else it.reversed()
}
}
playlistAdapter = PlaylistAdapter(videos.toMutableList(), playlistId!!, playlistType)
binding.playlistRecView.adapter = playlistAdapter
// listen for playlist items to become deleted
playlistAdapter!!.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
ImageHelper.loadImage(
playlistFeed.firstOrNull()?.thumbnail ?: "",
binding.thumbnail
)
}
binding.playlistInfo.text = getChannelAndVideoString(playlist, playlistFeed.size)
}
})
binding.playlistScrollview.viewTreeObserver.addOnScrollChangedListener {
if (_binding?.playlistScrollview?.canScrollVertically(1) == false &&
!isLoading
) {
// append more playlists to the recycler view
if (playlistType != PlaylistType.PUBLIC) {
isLoading = true
playlistAdapter?.showMoreItems()
isLoading = false
} else {
fetchNextPage()
}
}
}
// listener for swiping to the left or right
if (playlistType != PlaylistType.PUBLIC) {
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(
viewHolder: RecyclerView.ViewHolder,
direction: Int
) {
val position = viewHolder.absoluteAdapterPosition
playlistAdapter!!.removeFromPlaylist(requireContext(), position)
}
}
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
}
}
@SuppressLint("StringFormatInvalid", "StringFormatMatches") @SuppressLint("StringFormatInvalid", "StringFormatMatches")
private fun getChannelAndVideoString(playlist: Playlist, count: Int): String { private fun getChannelAndVideoString(playlist: Playlist, count: Int): String {
return playlist.uploader?.let { return playlist.uploader?.let {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:autoMirrored="true"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp">
<path android:fillColor="@android:color/white"
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
</vector>

View File

@ -53,6 +53,15 @@
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" /> android:textStyle="bold" />
<ImageView
android:id="@+id/sortMenu"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_sort"
android:visibility="gone"/>
<ImageView <ImageView
android:id="@+id/optionsMenu" android:id="@+id/optionsMenu"
android:layout_width="20dp" android:layout_width="20dp"

View File

@ -127,7 +127,7 @@
android:paddingHorizontal="10dp" android:paddingHorizontal="10dp"
android:text="@string/most_recent" android:text="@string/most_recent"
android:textSize="16sp" android:textSize="16sp"
app:drawableEndCompat="@drawable/ic_arrow_downward" /> app:drawableEndCompat="@drawable/ic_sort" />
</FrameLayout> </FrameLayout>

View File

@ -286,6 +286,15 @@
<item>@string/yt_shorts</item> <item>@string/yt_shorts</item>
</string-array> </string-array>
<string-array name="playlistSortOptions">
<item>@string/least_recent</item>
<item>@string/most_recent</item>
<item>@string/duration</item>
<item>@string/duration_reversed</item>
<item>@string/playlist_name_az</item>
<item>@string/playlist_name_za</item>
</string-array>
<string-array name="requiredNetwork"> <string-array name="requiredNetwork">
<item>@string/network_all</item> <item>@string/network_all</item>
<item>@string/network_wifi</item> <item>@string/network_wifi</item>

View File

@ -408,6 +408,10 @@
<string name="play_automatically_summary">Start playing video automatically when selecting</string> <string name="play_automatically_summary">Start playing video automatically when selecting</string>
<string name="fullscreen_gestures">Enter/exit fullscreen gestures</string> <string name="fullscreen_gestures">Enter/exit fullscreen gestures</string>
<string name="go_to_video">Go to video</string> <string name="go_to_video">Go to video</string>
<string name="playlist_name_az">Playlist Name (A-Z)</string>
<string name="playlist_name_za">Playlist Name (Z-A)</string>
<string name="duration">Duration</string>
<string name="duration_reversed">Duration (reversed)</string>
<!-- Notification channel strings --> <!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string> <string name="download_channel_name">Download Service</string>