diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt index 46beb38ea..d2e716573 100644 --- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt @@ -38,6 +38,7 @@ object PreferenceKeys { const val ALTERNATIVE_VIDEOS_LAYOUT = "alternative_videos_layout" const val NEW_VIDEOS_BADGE = "new_videos_badge" const val PLAYLISTS_ORDER = "playlists_order" + const val PLAYLIST_SORT_ORDER = "playlist_sort_order" /** * Instance diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt index dee1de33a..286deccfd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt @@ -6,6 +6,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isGone import androidx.core.view.updatePadding import androidx.fragment.app.Fragment 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.StreamItem import com.github.libretube.constants.IntentData +import com.github.libretube.constants.PreferenceKeys import com.github.libretube.databinding.FragmentPlaylistBinding import com.github.libretube.db.DatabaseHolder 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.helpers.ImageHelper import com.github.libretube.helpers.NavigationHelper +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.adapters.PlaylistAdapter 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.util.PlayingQueue import kotlinx.coroutines.Dispatchers @@ -58,6 +62,11 @@ class PlaylistFragment : Fragment() { // view models 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?) { super.onCreate(savedInstanceState) @@ -139,6 +148,8 @@ class PlaylistFragment : Fragment() { binding.playlistInfo.text = getChannelAndVideoString(response, response.videos) + showPlaylistVideos(response) + // show playlist options binding.optionsMenu.setOnClickListener { PlaylistOptionsBottomSheet( @@ -197,67 +208,17 @@ class PlaylistFragment : Fragment() { keepQueue = true ) } - } + binding.sortMenu.isGone = false + binding.sortMenu.setOnClickListener { + val sortOptions = resources.getStringArray(R.array.playlistSortOptions) - playlistAdapter = PlaylistAdapter(playlistFeed, playlistId!!, playlistType) - - // 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(response, playlistFeed.size) + BaseBottomSheet().apply { + setSimpleItems(sortOptions.toList()) { index -> + selectedSortOrder = index + showPlaylistVideos(response) + } + }.show(childFragmentManager) } - }) - - 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) { @@ -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") private fun getChannelAndVideoString(playlist: Playlist, count: Int): String { return playlist.uploader?.let { diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml new file mode 100644 index 000000000..92645c879 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_playlist.xml b/app/src/main/res/layout/fragment_playlist.xml index 05f70a39b..f5605157e 100644 --- a/app/src/main/res/layout/fragment_playlist.xml +++ b/app/src/main/res/layout/fragment_playlist.xml @@ -53,6 +53,15 @@ android:textSize="20sp" android:textStyle="bold" /> + + + app:drawableEndCompat="@drawable/ic_sort" /> diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index 75a596075..c4a725c8b 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -286,6 +286,15 @@ @string/yt_shorts + + @string/least_recent + @string/most_recent + @string/duration + @string/duration_reversed + @string/playlist_name_az + @string/playlist_name_za + + @string/network_all @string/network_wifi diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f622d1d8f..4ad944fe0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -408,6 +408,10 @@ Start playing video automatically when selecting Enter/exit fullscreen gestures Go to video + Playlist Name (A-Z) + Playlist Name (Z-A) + Duration + Duration (reversed) Download Service