From d6232b1ddcd1575b8c5680be17e719cba0abb7be Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 11:12:02 +0200 Subject: [PATCH 01/13] Playing queue layout --- .../ui/adapters/PlayingQueueAdapter.kt | 7 ++ .../libretube/ui/sheets/PlayingQueueSheet.kt | 27 ++++++++ .../ui/viewholders/PlayingQueueViewHolder.kt | 8 +++ app/src/main/res/layout/queue_row.xml | 65 +++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt create mode 100644 app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt create mode 100644 app/src/main/java/com/github/libretube/ui/viewholders/PlayingQueueViewHolder.kt create mode 100644 app/src/main/res/layout/queue_row.xml diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt new file mode 100644 index 000000000..fe5c96302 --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -0,0 +1,7 @@ +package com.github.libretube.ui.adapters + +import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.ui.viewholders.PlayingQueueViewHolder + +class PlayingQueueAdapter: RecyclerView.Adapter { +} \ No newline at end of file diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt new file mode 100644 index 000000000..e65fd3a2a --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -0,0 +1,27 @@ +package com.github.libretube.ui.sheets + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.github.libretube.databinding.BottomSheetBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class PlayingQueueSheet: BottomSheetDialogFragment() { + private lateinit var binding: BottomSheetBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = BottomSheetBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + + } +} diff --git a/app/src/main/java/com/github/libretube/ui/viewholders/PlayingQueueViewHolder.kt b/app/src/main/java/com/github/libretube/ui/viewholders/PlayingQueueViewHolder.kt new file mode 100644 index 000000000..9fe6f200d --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/viewholders/PlayingQueueViewHolder.kt @@ -0,0 +1,8 @@ +package com.github.libretube.ui.viewholders + +import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.databinding.QueueRowBinding + +class PlayingQueueViewHolder( + val binding: QueueRowBinding +) : RecyclerView.ViewHolder(binding.root) diff --git a/app/src/main/res/layout/queue_row.xml b/app/src/main/res/layout/queue_row.xml new file mode 100644 index 000000000..cab788305 --- /dev/null +++ b/app/src/main/res/layout/queue_row.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From afe25fc67a0016c43784f1d113a64287c8f6e1f2 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 11:33:35 +0200 Subject: [PATCH 02/13] Queue bottom sheet in player fragment --- .../ui/adapters/PlayingQueueAdapter.kt | 34 +++++- .../libretube/ui/fragments/HomeFragment.kt | 3 + .../libretube/ui/fragments/PlayerFragment.kt | 5 + .../libretube/ui/sheets/PlayingQueueSheet.kt | 10 +- .../com/github/libretube/util/PlayingQueue.kt | 3 + app/src/main/res/layout/fragment_player.xml | 115 ++++++++++++------ app/src/main/res/layout/queue_row.xml | 10 +- app/src/main/res/values/strings.xml | 1 + 8 files changed, 136 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index fe5c96302..83363a80b 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -1,7 +1,37 @@ package com.github.libretube.ui.adapters +import android.text.format.DateUtils +import android.view.LayoutInflater +import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.api.obj.StreamItem +import com.github.libretube.databinding.QueueRowBinding import com.github.libretube.ui.viewholders.PlayingQueueViewHolder +import com.github.libretube.util.ImageHelper -class PlayingQueueAdapter: RecyclerView.Adapter { -} \ No newline at end of file +class PlayingQueueAdapter( + private val items: List +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder { + val binding = QueueRowBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return PlayingQueueViewHolder(binding) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onBindViewHolder(holder: PlayingQueueViewHolder, position: Int) { + val streamItem = items[position] + holder.binding.apply { + ImageHelper.loadImage(streamItem.thumbnail, thumbnail) + title.text = streamItem.title + uploader.text = streamItem.uploaderName + duration.text = streamItem.duration?.let { DateUtils.formatElapsedTime(it) } + } + } +} diff --git a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt index c5a7566cf..ca5b8a0ff 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt @@ -20,6 +20,7 @@ import com.github.libretube.ui.adapters.ChannelAdapter import com.github.libretube.ui.adapters.TrendingAdapter import com.github.libretube.ui.base.BaseFragment import com.github.libretube.util.LocaleHelper +import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.google.android.material.snackbar.Snackbar import retrofit2.HttpException @@ -80,6 +81,8 @@ class HomeFragment : BaseFragment() { } finally { binding.homeRefresh.isRefreshing = false } + // TODO() REMOVE TESTING + PlayingQueue.streams = response runOnUiThread { binding.progressBar.visibility = View.GONE diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 5606b1bbb..7a639500f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -64,6 +64,7 @@ import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.dialogs.AddToPlaylistDialog import com.github.libretube.ui.dialogs.DownloadDialog import com.github.libretube.ui.dialogs.ShareDialog +import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.ui.views.BottomSheet import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.BackgroundHelper @@ -409,6 +410,10 @@ class PlayerFragment : BaseFragment() { toggleComments() } + binding.queueToggle.setOnClickListener { + PlayingQueueSheet().show(childFragmentManager, null) + } + // FullScreen button trigger // hide fullscreen button if auto rotation enabled playerBinding.fullscreen.visibility = diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index e65fd3a2a..e76dc62b3 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -4,10 +4,13 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager import com.github.libretube.databinding.BottomSheetBinding +import com.github.libretube.ui.adapters.PlayingQueueAdapter +import com.github.libretube.util.PlayingQueue import com.google.android.material.bottomsheet.BottomSheetDialogFragment -class PlayingQueueSheet: BottomSheetDialogFragment() { +class PlayingQueueSheet : BottomSheetDialogFragment() { private lateinit var binding: BottomSheetBinding override fun onCreateView( @@ -22,6 +25,9 @@ class PlayingQueueSheet: BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + binding.optionsRecycler.layoutManager = LinearLayoutManager(context) + binding.optionsRecycler.adapter = PlayingQueueAdapter( + PlayingQueue.streams + ) } } diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 7063c1d7d..3938eb437 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -1,8 +1,11 @@ package com.github.libretube.util +import com.github.libretube.api.obj.StreamItem + object PlayingQueue { private val queue = mutableListOf() private var currentVideoId: String? = null + var streams: List = listOf() fun add(videoId: String) { if (currentVideoId == videoId) return diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 01cfb7bdd..a0680cc65 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -298,6 +298,81 @@ app:cornerRadius="11dp" /> + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - @@ -54,7 +55,7 @@ android:id="@+id/duration" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="10dp" + android:layout_marginStart="15dp" android:textSize="14sp" tools:text="10:00" /> @@ -62,4 +63,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ece153900..3d097ad91 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -348,6 +348,7 @@ Show more Time code Added to playlist + Playing queue Download Service From 41162fafac7c6969e9ec243f680c4ad700fcb9c0 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 13:39:15 +0200 Subject: [PATCH 03/13] Add functionality --- .../libretube/extensions/ToStreamItem.kt | 22 ++++++++ .../libretube/services/BackgroundMode.kt | 13 +++-- .../libretube/ui/fragments/HomeFragment.kt | 2 - .../libretube/ui/fragments/PlayerFragment.kt | 11 ++-- .../libretube/ui/sheets/PlayingQueueSheet.kt | 2 +- .../ui/sheets/VideoOptionsBottomSheet.kt | 27 +++++++++- .../github/libretube/util/AutoPlayHelper.kt | 28 +--------- .../com/github/libretube/util/PlayingQueue.kt | 53 ++++++++++--------- 8 files changed, 95 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt diff --git a/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt new file mode 100644 index 000000000..f58d4d298 --- /dev/null +++ b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt @@ -0,0 +1,22 @@ +package com.github.libretube.extensions + +import com.github.libretube.api.obj.StreamItem +import com.github.libretube.api.obj.Streams + +fun Streams?.toStreamItem(videoId: String): StreamItem { + if (this == null) return StreamItem() + return StreamItem( + url = videoId, + title = title, + thumbnail = thumbnailUrl, + uploaderName = uploader, + uploaderUrl = uploaderUrl, + uploaderAvatar = uploaderAvatar, + uploadedDate = uploadDate, + uploaded = null, + duration = duration, + views = views, + uploaderVerified = uploaderVerified, + shortDescription = description + ) +} diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 9cda3e3cc..f6b617682 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -25,6 +25,7 @@ import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHolder import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.toID +import com.github.libretube.extensions.toStreamItem import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper @@ -146,7 +147,9 @@ class BackgroundMode : Service() { } private fun updateWatchPosition() { - player?.currentPosition?.let { DatabaseHelper.saveWatchPosition(videoId, it) } + player?.currentPosition?.let { + DatabaseHelper.saveWatchPosition(videoId, it) + } handler.postDelayed(this::updateWatchPosition, 500) } @@ -158,7 +161,7 @@ class BackgroundMode : Service() { seekToPosition: Long = 0 ) { // append the video to the playing queue - PlayingQueue.add(videoId) + PlayingQueue.add(streams.toStreamItem(videoId)) CoroutineScope(Dispatchers.IO).launch { try { streams = RetrofitInstance.api.getStreams(videoId) @@ -166,6 +169,8 @@ class BackgroundMode : Service() { return@launch } + PlayingQueue.updateCurrent(streams.toStreamItem(videoId)) + handler.post { playAudio(seekToPosition) } @@ -175,8 +180,6 @@ class BackgroundMode : Service() { private fun playAudio( seekToPosition: Long ) { - PlayingQueue.updateCurrent(videoId) - initializePlayer() setMediaItem() @@ -280,7 +283,7 @@ class BackgroundMode : Service() { if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!) // search for the next videoId in the playlist CoroutineScope(Dispatchers.IO).launch { - nextStreamId = autoPlayHelper.getNextVideoId(videoId, streams!!.relatedStreams!!) + nextStreamId = autoPlayHelper.getNextVideoId(videoId) } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt index ca5b8a0ff..38dc23e3f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt @@ -81,8 +81,6 @@ class HomeFragment : BaseFragment() { } finally { binding.homeRefresh.isRefreshing = false } - // TODO() REMOVE TESTING - PlayingQueue.streams = response runOnUiThread { binding.progressBar.visibility = View.GONE diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 7a639500f..fa360bbb5 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -52,6 +52,7 @@ import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.hideKeyboard import com.github.libretube.extensions.query import com.github.libretube.extensions.toID +import com.github.libretube.extensions.toStreamItem import com.github.libretube.models.PlayerViewModel import com.github.libretube.models.interfaces.PlayerOptionsInterface import com.github.libretube.services.BackgroundMode @@ -635,8 +636,6 @@ class PlayerFragment : BaseFragment() { private fun playVideo() { lifecycleScope.launchWhenCreated { - PlayingQueue.updateCurrent(videoId!!) - streams = try { RetrofitInstance.api.getStreams(videoId!!) } catch (e: IOException) { @@ -650,6 +649,12 @@ class PlayerFragment : BaseFragment() { return@launchWhenCreated } + PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) + + if (PlayingQueue.size() <= 1) PlayingQueue.add( + *streams.relatedStreams.orEmpty().toTypedArray() + ) + runOnUiThread { // hide the button to skip SponsorBlock segments manually binding.sbSkipBtn.visibility = View.GONE @@ -694,7 +699,7 @@ class PlayerFragment : BaseFragment() { if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId) // search for the next videoId in the playlist lifecycleScope.launchWhenCreated { - nextStreamId = autoPlayHelper.getNextVideoId(videoId!!, streams.relatedStreams) + nextStreamId = autoPlayHelper.getNextVideoId(videoId!!) } } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index e76dc62b3..87d6c8c8b 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -27,7 +27,7 @@ class PlayingQueueSheet : BottomSheetDialogFragment() { binding.optionsRecycler.layoutManager = LinearLayoutManager(context) binding.optionsRecycler.adapter = PlayingQueueAdapter( - PlayingQueue.streams + PlayingQueue.getStreams() ) } } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt index f174e2a33..5310b75a1 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt @@ -3,8 +3,10 @@ package com.github.libretube.ui.sheets import android.os.Bundle import android.widget.Toast import com.github.libretube.R +import com.github.libretube.api.RetrofitInstance import com.github.libretube.constants.IntentData import com.github.libretube.constants.ShareObjectType +import com.github.libretube.extensions.toStreamItem import com.github.libretube.ui.dialogs.AddToPlaylistDialog import com.github.libretube.ui.dialogs.DownloadDialog import com.github.libretube.ui.dialogs.ShareDialog @@ -12,6 +14,9 @@ import com.github.libretube.ui.views.BottomSheet import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * Dialog with different options for a selected video. @@ -79,10 +84,28 @@ class VideoOptionsBottomSheet( shareDialog.show(parentFragmentManager, ShareDialog::class.java.name) } context?.getString(R.string.play_next) -> { - PlayingQueue.addAsNext(videoId) + CoroutineScope(Dispatchers.IO).launch { + try { + PlayingQueue.addAsNext( + RetrofitInstance.api.getStreams(videoId) + .toStreamItem(videoId) + ) + } catch (e: Exception) { + e.printStackTrace() + } + } } context?.getString(R.string.add_to_queue) -> { - PlayingQueue.add(videoId) + CoroutineScope(Dispatchers.IO).launch { + try { + PlayingQueue.add( + RetrofitInstance.api.getStreams(videoId) + .toStreamItem(videoId) + ) + } catch (e: Exception) { + e.printStackTrace() + } + } } } } diff --git a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt index 1e1f17e1f..645d58fa9 100644 --- a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt +++ b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt @@ -16,13 +16,10 @@ class AutoPlayHelper( * get the id of the next video to be played */ suspend fun getNextVideoId( - currentVideoId: String, - relatedStreams: List? + currentVideoId: String ): String? { return if (playlistId == null) { - getNextTrendingVideoId( - relatedStreams - ) + null } else { getNextPlaylistVideoId( currentVideoId @@ -30,27 +27,6 @@ class AutoPlayHelper( } } - /** - * get the id of the next related video - */ - private fun getNextTrendingVideoId( - relatedStreams: List? - ): String? { - // don't play a video if it got played before already - if (relatedStreams == null || relatedStreams.isEmpty()) return null - var index = 0 - var nextStreamId: String? = null - while (nextStreamId == null || PlayingQueue.containsBeforeCurrent(nextStreamId)) { - nextStreamId = relatedStreams[index].url!!.toID() - if (index + 1 < relatedStreams.size) { - index += 1 - } else { - break - } - } - return nextStreamId - } - /** * get the videoId of the next video in a playlist */ diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 3938eb437..cb827f89d 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -1,58 +1,63 @@ package com.github.libretube.util import com.github.libretube.api.obj.StreamItem +import com.github.libretube.extensions.toID object PlayingQueue { - private val queue = mutableListOf() - private var currentVideoId: String? = null - var streams: List = listOf() + private val queue = mutableListOf() + private var currentStream: StreamItem? = null - fun add(videoId: String) { - if (currentVideoId == videoId) return - if (queue.contains(videoId)) queue.remove(videoId) - queue.add(videoId) + fun add(vararg streamItem: StreamItem) { + streamItem.forEach { + if (currentStream != it) { + if (queue.contains(it)) queue.remove(it) + queue.add(it) + } + } } - fun addAsNext(videoId: String) { - if (currentVideoId == videoId) return - if (queue.contains(videoId)) queue.remove(videoId) + fun addAsNext(streamItem: StreamItem) { + if (currentStream == streamItem) return + if (queue.contains(streamItem)) queue.remove(streamItem) queue.add( - queue.indexOf(currentVideoId) + 1, - videoId + currentIndex() + 1, + streamItem ) } fun getNext(): String? { return try { - queue[currentIndex() + 1] + queue[currentIndex() + 1].url?.toID() } catch (e: Exception) { null } } fun getPrev(): String? { - val index = queue.indexOf(currentVideoId) - return if (index > 0) queue[index - 1] else null + val index = queue.indexOf(currentStream) + return if (index > 0) queue[index - 1].url?.toID() else null } fun hasPrev(): Boolean { - return queue.indexOf(currentVideoId) > 0 + return queue.indexOf(currentStream) > 0 } - fun updateCurrent(videoId: String) { - currentVideoId = videoId - queue.add(videoId) + fun updateCurrent(streamItem: StreamItem) { + currentStream = streamItem + queue.add(streamItem) } fun isNotEmpty() = queue.isNotEmpty() + fun isEmpty() = queue.isEmpty() + fun clear() = queue.clear() - fun currentIndex() = queue.indexOf(currentVideoId) + fun size() = queue.size - fun contains(videoId: String) = queue.contains(videoId) + private fun currentIndex() = queue.indexOf(currentStream) - fun containsBeforeCurrent(videoId: String): Boolean { - return queue.contains(videoId) && queue.indexOf(videoId) < currentIndex() - } + fun contains(streamItem: StreamItem) = queue.contains(streamItem) + + fun getStreams() = queue } From 02d4321a2dc1ecd2ac57d489dd1d7a0a4d30edfc Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 14:43:47 +0200 Subject: [PATCH 04/13] Replace the autoPlayHelper with the PlayingQueue --- .../libretube/services/BackgroundMode.kt | 48 +++---------- .../ui/adapters/PlayingQueueAdapter.kt | 14 ++-- .../libretube/ui/fragments/HomeFragment.kt | 1 - .../libretube/ui/fragments/PlayerFragment.kt | 47 +++++-------- .../libretube/ui/sheets/PlayingQueueSheet.kt | 5 +- .../github/libretube/util/AutoPlayHelper.kt | 68 ------------------- .../com/github/libretube/util/PlayingQueue.kt | 42 +++++++++++- 7 files changed, 74 insertions(+), 151 deletions(-) delete mode 100644 app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index f6b617682..3faab91e9 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -24,9 +24,7 @@ import com.github.libretube.constants.PreferenceKeys import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHolder import com.github.libretube.extensions.awaitQuery -import com.github.libretube.extensions.toID import com.github.libretube.extensions.toStreamItem -import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper import com.github.libretube.util.PlayingQueue @@ -81,16 +79,6 @@ class BackgroundMode : Service() { */ private lateinit var nowPlayingNotification: NowPlayingNotification - /** - * The [videoId] of the next stream for autoplay - */ - private var nextStreamId: String? = null - - /** - * Helper for finding the next video in the playlist - */ - private lateinit var autoPlayHelper: AutoPlayHelper - /** * Autoplay Preference */ @@ -133,9 +121,6 @@ class BackgroundMode : Service() { playlistId = intent.getStringExtra(IntentData.playlistId) val position = intent.getLongExtra(IntentData.position, 0L) - // initialize the playlist autoPlay Helper - autoPlayHelper = AutoPlayHelper(playlistId) - // play the audio in the background loadAudio(videoId, position) @@ -169,7 +154,12 @@ class BackgroundMode : Service() { return@launch } - PlayingQueue.updateCurrent(streams.toStreamItem(videoId)) + // add the playlist video to the queue + if (playlistId != null && PlayingQueue.isEmpty()) { + PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId)) + } else { + PlayingQueue.updateCurrent(streams.toStreamItem(videoId)) + } handler.post { playAudio(seekToPosition) @@ -221,8 +211,6 @@ class BackgroundMode : Service() { player?.setPlaybackSpeed(playbackSpeed) fetchSponsorBlockSegments() - - if (PlayerHelper.autoPlayEnabled) setNextStream() } /** @@ -271,32 +259,16 @@ class BackgroundMode : Service() { }) } - /** - * set the videoId of the next stream for autoplay - */ - private fun setNextStream() { - if (streams!!.relatedStreams!!.isNotEmpty()) { - nextStreamId = streams?.relatedStreams!![0].url!!.toID() - } - - if (playlistId == null) return - if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!) - // search for the next videoId in the playlist - CoroutineScope(Dispatchers.IO).launch { - nextStreamId = autoPlayHelper.getNextVideoId(videoId) - } - } - /** * Plays the first related video to the current (used when the playback of the current video ended) */ private fun playNextVideo() { - if (nextStreamId == null || nextStreamId == videoId) return - val nextQueueVideo = PlayingQueue.getNext() - if (nextQueueVideo != null) nextStreamId = nextQueueVideo + val nextVideo = PlayingQueue.getNext() // play new video on background - this.videoId = nextStreamId!! + if (nextVideo != null) { + this.videoId = nextVideo + } this.segmentData = null loadAudio(videoId) } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index 83363a80b..fae141ffc 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -4,14 +4,12 @@ import android.text.format.DateUtils import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.github.libretube.api.obj.StreamItem import com.github.libretube.databinding.QueueRowBinding import com.github.libretube.ui.viewholders.PlayingQueueViewHolder import com.github.libretube.util.ImageHelper +import com.github.libretube.util.PlayingQueue -class PlayingQueueAdapter( - private val items: List -) : RecyclerView.Adapter() { +class PlayingQueueAdapter() : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder { val binding = QueueRowBinding.inflate( LayoutInflater.from(parent.context), @@ -22,16 +20,18 @@ class PlayingQueueAdapter( } override fun getItemCount(): Int { - return items.size + return PlayingQueue.size() } override fun onBindViewHolder(holder: PlayingQueueViewHolder, position: Int) { - val streamItem = items[position] + val streamItem = PlayingQueue.getStreams()[position] holder.binding.apply { ImageHelper.loadImage(streamItem.thumbnail, thumbnail) title.text = streamItem.title uploader.text = streamItem.uploaderName - duration.text = streamItem.duration?.let { DateUtils.formatElapsedTime(it) } + duration.text = streamItem.duration?.let { + DateUtils.formatElapsedTime(it) + } } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt index 38dc23e3f..c5a7566cf 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt @@ -20,7 +20,6 @@ import com.github.libretube.ui.adapters.ChannelAdapter import com.github.libretube.ui.adapters.TrendingAdapter import com.github.libretube.ui.base.BaseFragment import com.github.libretube.util.LocaleHelper -import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.google.android.material.snackbar.Snackbar import retrofit2.HttpException diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index fa360bbb5..b0ad5a07f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -67,7 +67,6 @@ import com.github.libretube.ui.dialogs.DownloadDialog import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.ui.views.BottomSheet -import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.ImageHelper import com.github.libretube.util.NowPlayingNotification @@ -155,12 +154,6 @@ class PlayerFragment : BaseFragment() { private var token = PreferenceHelper.getToken() private var videoShownInExternalPlayer = false - /** - * for autoplay - */ - private var nextStreamId: String? = null - private lateinit var autoPlayHelper: AutoPlayHelper - /** * for the player notification */ @@ -649,11 +642,20 @@ class PlayerFragment : BaseFragment() { return@launchWhenCreated } - PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) - - if (PlayingQueue.size() <= 1) PlayingQueue.add( - *streams.relatedStreams.orEmpty().toTypedArray() - ) + if (PlayingQueue.size() == 0) { + CoroutineScope(Dispatchers.IO).launch { + if (playlistId != null) { + PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId!!)) + } else { + PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) + PlayingQueue.add( + *streams.relatedStreams.orEmpty().toTypedArray() + ) + } + } + } else { + PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) + } runOnUiThread { // hide the button to skip SponsorBlock segments manually @@ -678,8 +680,6 @@ class PlayerFragment : BaseFragment() { if (PlayerHelper.sponsorBlockEnabled) fetchSponsorBlockSegments() // show comments if related streams disabled if (!PlayerHelper.relatedStreamsEnabled) toggleComments() - // prepare for autoplay - if (binding.player.autoplayEnabled) setNextStream() // add the video to the watch history if (PlayerHelper.watchHistoryEnabled) { @@ -692,17 +692,6 @@ class PlayerFragment : BaseFragment() { } } - /** - * set the videoId of the next stream for autoplay - */ - private fun setNextStream() { - if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId) - // search for the next videoId in the playlist - lifecycleScope.launchWhenCreated { - nextStreamId = autoPlayHelper.getNextVideoId(videoId!!) - } - } - /** * fetch the segments for SponsorBlock */ @@ -768,17 +757,14 @@ class PlayerFragment : BaseFragment() { // used for autoplay and skipping to next video private fun playNextVideo() { - if (nextStreamId == null) return - // check whether there is a new video in the queue - val nextQueueVideo = PlayingQueue.getNext() - if (nextQueueVideo != null) nextStreamId = nextQueueVideo + val nextVideo = PlayingQueue.getNext() // by making sure that the next and the current video aren't the same saveWatchPosition() // forces the comments to reload for the new video commentsLoaded = false binding.commentsRecView.adapter = null // save the id of the next stream as videoId and load the next video - videoId = nextStreamId + videoId = nextVideo playVideo() } @@ -876,7 +862,6 @@ class PlayerFragment : BaseFragment() { @Suppress("DEPRECATION") if ( playbackState == Player.STATE_ENDED && - nextStreamId != null && !transitioning && binding.player.autoplayEnabled ) { diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index 87d6c8c8b..fa09f5913 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import com.github.libretube.databinding.BottomSheetBinding import com.github.libretube.ui.adapters.PlayingQueueAdapter -import com.github.libretube.util.PlayingQueue import com.google.android.material.bottomsheet.BottomSheetDialogFragment class PlayingQueueSheet : BottomSheetDialogFragment() { @@ -26,8 +25,6 @@ class PlayingQueueSheet : BottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) binding.optionsRecycler.layoutManager = LinearLayoutManager(context) - binding.optionsRecycler.adapter = PlayingQueueAdapter( - PlayingQueue.getStreams() - ) + binding.optionsRecycler.adapter = PlayingQueueAdapter() } } diff --git a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt deleted file mode 100644 index 645d58fa9..000000000 --- a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.github.libretube.util - -import com.github.libretube.api.RetrofitInstance -import com.github.libretube.extensions.toID -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -class AutoPlayHelper( - private val playlistId: String? -) { - - private val playlistStreamIds = mutableListOf() - private var playlistNextPage: String? = null - - /** - * get the id of the next video to be played - */ - suspend fun getNextVideoId( - currentVideoId: String - ): String? { - return if (playlistId == null) { - null - } else { - getNextPlaylistVideoId( - currentVideoId - ) - } - } - - /** - * get the videoId of the next video in a playlist - */ - private suspend fun getNextPlaylistVideoId(currentVideoId: String): String? { - // if the playlists contain the video, then save the next video as next stream - if (playlistStreamIds.contains(currentVideoId)) { - val index = playlistStreamIds.indexOf(currentVideoId) - // check whether there's a next video - return if (index + 1 < playlistStreamIds.size) { - playlistStreamIds[index + 1] - } else if (playlistNextPage == null) { - null - } else { - getNextPlaylistVideoId(currentVideoId) - } - } else if (playlistStreamIds.isEmpty() || playlistNextPage != null) { - // fetch the next page of the playlist - return withContext(Dispatchers.IO) { - // fetch the playlists or its nextPage's videos - val playlist = - if (playlistNextPage == null) { - RetrofitInstance.authApi.getPlaylist(playlistId!!) - } else { - RetrofitInstance.authApi.getPlaylistNextPage( - playlistId!!, - playlistNextPage!! - ) - } - // save the playlist urls to the list - playlistStreamIds += playlist.relatedStreams!!.map { it.url!!.toID() } - // save playlistNextPage for usage if video is not contained - playlistNextPage = playlist.nextpage - return@withContext getNextPlaylistVideoId(currentVideoId) - } - } - // return null when no nextPage is found - return null - } -} diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index cb827f89d..2c12ceddc 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -1,7 +1,11 @@ package com.github.libretube.util +import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.StreamItem import com.github.libretube.extensions.toID +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch object PlayingQueue { private val queue = mutableListOf() @@ -44,7 +48,7 @@ object PlayingQueue { fun updateCurrent(streamItem: StreamItem) { currentStream = streamItem - queue.add(streamItem) + if (!contains(streamItem)) queue.add(streamItem) } fun isNotEmpty() = queue.isNotEmpty() @@ -57,7 +61,41 @@ object PlayingQueue { private fun currentIndex() = queue.indexOf(currentStream) - fun contains(streamItem: StreamItem) = queue.contains(streamItem) + fun contains(streamItem: StreamItem) = queue.any { it.url?.toID() == streamItem.url?.toID() } fun getStreams() = queue + + private fun fetchMoreFromPlaylist(playlistId: String, nextPage: String?) { + var playlistNextPage: String? = nextPage + CoroutineScope(Dispatchers.IO).launch { + while (playlistNextPage != null) { + RetrofitInstance.authApi.getPlaylistNextPage( + playlistId, + playlistNextPage!! + ).apply { + add( + *this.relatedStreams.orEmpty().toTypedArray() + ) + playlistNextPage = this.nextpage + } + } + } + } + + fun insertPlaylist(playlistId: String, newCurrentStream: StreamItem) { + CoroutineScope(Dispatchers.IO).launch { + try { + val response = RetrofitInstance.authApi.getPlaylist(playlistId) + add( + *response.relatedStreams + .orEmpty() + .toTypedArray() + ) + updateCurrent(newCurrentStream) + fetchMoreFromPlaylist(playlistId, response.nextpage) + } catch (e: Exception) { + e.printStackTrace() + } + } + } } From e7ef03047339203db8a5e3f9ee0f03b9ce14756b Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 14:54:02 +0200 Subject: [PATCH 05/13] Add swipe gestures to remove from queue --- .../libretube/ui/sheets/PlayingQueueSheet.kt | 33 ++++++++++++++++++- .../com/github/libretube/util/PlayingQueue.kt | 4 ++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index fa09f5913..0c3f33d60 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -4,9 +4,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.github.libretube.databinding.BottomSheetBinding import com.github.libretube.ui.adapters.PlayingQueueAdapter +import com.github.libretube.util.PlayingQueue import com.google.android.material.bottomsheet.BottomSheetDialogFragment class PlayingQueueSheet : BottomSheetDialogFragment() { @@ -25,6 +28,34 @@ class PlayingQueueSheet : BottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) binding.optionsRecycler.layoutManager = LinearLayoutManager(context) - binding.optionsRecycler.adapter = PlayingQueueAdapter() + val adapter = PlayingQueueAdapter() + binding.optionsRecycler.adapter = adapter + + val callback = object : ItemTouchHelper.SimpleCallback( + 0, + ItemTouchHelper.LEFT + ) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + TODO("Not yet implemented") + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val position = viewHolder.absoluteAdapterPosition + if (position == PlayingQueue.currentIndex()) { + adapter.notifyItemChanged(position) + return + } + PlayingQueue.remove(position) + adapter.notifyItemRemoved(position) + adapter.notifyItemRangeChanged(position, adapter.itemCount) + } + } + + val itemTouchHelper = ItemTouchHelper(callback) + itemTouchHelper.attachToRecyclerView(binding.optionsRecycler) } } diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 2c12ceddc..e825ae63a 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -59,12 +59,14 @@ object PlayingQueue { fun size() = queue.size - private fun currentIndex() = queue.indexOf(currentStream) + fun currentIndex() = queue.indexOf(currentStream) fun contains(streamItem: StreamItem) = queue.any { it.url?.toID() == streamItem.url?.toID() } fun getStreams() = queue + fun remove(index: Int) = queue.removeAt(index) + private fun fetchMoreFromPlaylist(playlistId: String, nextPage: String?) { var playlistNextPage: String? = nextPage CoroutineScope(Dispatchers.IO).launch { From 1962268460a88755b32b32f7ed8b23b3bc403541 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:09:58 +0200 Subject: [PATCH 06/13] Dragging and dropping items --- .../java/com/github/libretube/extensions/Move.kt | 7 +++++++ .../libretube/ui/adapters/PlayingQueueAdapter.kt | 8 +++++++- .../libretube/ui/sheets/PlayingQueueSheet.kt | 15 ++++++++++++++- .../com/github/libretube/util/PlayingQueue.kt | 3 +++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/extensions/Move.kt diff --git a/app/src/main/java/com/github/libretube/extensions/Move.kt b/app/src/main/java/com/github/libretube/extensions/Move.kt new file mode 100644 index 000000000..bd556be87 --- /dev/null +++ b/app/src/main/java/com/github/libretube/extensions/Move.kt @@ -0,0 +1,7 @@ +package com.github.libretube.extensions + +fun MutableList.move(oldPosition: Int, newPosition: Int) { + val item = this.get(oldPosition) + this.removeAt(oldPosition) + this.add(newPosition, item) +} diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index fae141ffc..77aa4bf5b 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -8,8 +8,9 @@ import com.github.libretube.databinding.QueueRowBinding import com.github.libretube.ui.viewholders.PlayingQueueViewHolder import com.github.libretube.util.ImageHelper import com.github.libretube.util.PlayingQueue +import com.github.libretube.util.ThemeHelper -class PlayingQueueAdapter() : RecyclerView.Adapter() { +class PlayingQueueAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder { val binding = QueueRowBinding.inflate( LayoutInflater.from(parent.context), @@ -32,6 +33,11 @@ class PlayingQueueAdapter() : RecyclerView.Adapter() { duration.text = streamItem.duration?.let { DateUtils.formatElapsedTime(it) } + if (PlayingQueue.currentIndex() == position) { + root.setBackgroundColor( + ThemeHelper.getThemeColor(root.context, android.R.attr.colorControlHighlight) + ) + } } } } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index 0c3f33d60..71f3de37e 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -35,12 +35,25 @@ class PlayingQueueSheet : BottomSheetDialogFragment() { 0, ItemTouchHelper.LEFT ) { + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN + return makeMovementFlags(dragFlags, 0) + } + override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { - TODO("Not yet implemented") + val from = viewHolder.absoluteAdapterPosition + val to = target.absoluteAdapterPosition + + adapter.notifyItemMoved(from, to) + PlayingQueue.move(from, to) + return true } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index e825ae63a..578843554 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -2,6 +2,7 @@ package com.github.libretube.util import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.StreamItem +import com.github.libretube.extensions.move import com.github.libretube.extensions.toID import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -67,6 +68,8 @@ object PlayingQueue { fun remove(index: Int) = queue.removeAt(index) + fun move(from: Int, to: Int) = queue.move(from, to) + private fun fetchMoreFromPlaylist(playlistId: String, nextPage: String?) { var playlistNextPage: String? = nextPage CoroutineScope(Dispatchers.IO).launch { From fb21dd254319215bac35b2323e1c6cc1732b283b Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:19:14 +0200 Subject: [PATCH 07/13] Minor fixes --- .../github/libretube/ui/sheets/PlayingQueueSheet.kt | 10 +--------- .../java/com/github/libretube/util/PlayingQueue.kt | 12 +++++++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt index 71f3de37e..1292b7377 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlayingQueueSheet.kt @@ -32,17 +32,9 @@ class PlayingQueueSheet : BottomSheetDialogFragment() { binding.optionsRecycler.adapter = adapter val callback = object : ItemTouchHelper.SimpleCallback( - 0, + ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT ) { - override fun getMovementFlags( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder - ): Int { - val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN - return makeMovementFlags(dragFlags, 0) - } - override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 578843554..863cd004a 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -1,5 +1,6 @@ package com.github.libretube.util +import android.util.Log import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.StreamItem import com.github.libretube.extensions.move @@ -32,6 +33,7 @@ object PlayingQueue { fun getNext(): String? { return try { + Log.e("new video", queue[currentIndex() + 1].toString()) queue[currentIndex() + 1].url?.toID() } catch (e: Exception) { null @@ -60,7 +62,15 @@ object PlayingQueue { fun size() = queue.size - fun currentIndex() = queue.indexOf(currentStream) + fun currentIndex(): Int { + return try { + queue.indexOf( + queue.first { it.url?.toID() == currentStream?.url?.toID() } + ) + } catch (e: Exception) { + 0 + } + } fun contains(streamItem: StreamItem) = queue.any { it.url?.toID() == streamItem.url?.toID() } From b5c6a27264822a9fdb10397b44e8342de728ec8a Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:22:36 +0200 Subject: [PATCH 08/13] Fix a crash when the queue is empty --- .../libretube/ui/fragments/PlayerFragment.kt | 16 ++++++++++------ app/src/main/res/values/strings.xml | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index b0ad5a07f..398800cc0 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -757,15 +757,19 @@ class PlayerFragment : BaseFragment() { // used for autoplay and skipping to next video private fun playNextVideo() { - val nextVideo = PlayingQueue.getNext() + val nextVideoId = PlayingQueue.getNext() // by making sure that the next and the current video aren't the same saveWatchPosition() - // forces the comments to reload for the new video - commentsLoaded = false - binding.commentsRecView.adapter = null + // save the id of the next stream as videoId and load the next video - videoId = nextVideo - playVideo() + if (nextVideoId != null) { + videoId = nextVideoId + + // forces the comments to reload for the new video + commentsLoaded = false + binding.commentsRecView.adapter = null + playVideo() + } } private fun prepareExoPlayerView() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d097ad91..e69f7711e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -349,6 +349,7 @@ Time code Added to playlist Playing queue + Queue Download Service From 220f131cb82398c9ccd974ba9e4ba52165408515 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:49:12 +0200 Subject: [PATCH 09/13] Open queue from navbar menu --- .../libretube/ui/activities/MainActivity.kt | 15 +++++++++++ .../ui/adapters/PlayingQueueAdapter.kt | 9 ++++--- .../com/github/libretube/util/PlayingQueue.kt | 1 - app/src/main/res/layout/queue_row.xml | 27 +++++-------------- app/src/main/res/menu/action_bar.xml | 6 +++++ 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index 32286d1dd..bdf30f72c 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -8,6 +8,7 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper +import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View @@ -34,8 +35,10 @@ import com.github.libretube.services.ClosingService import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.dialogs.ErrorDialog import com.github.libretube.ui.fragments.PlayerFragment +import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.util.NavBarHelper import com.github.libretube.util.NetworkHelper +import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.github.libretube.util.ThemeHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -195,6 +198,11 @@ class MainActivity : BaseActivity() { ) } + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + menu?.findItem(R.id.action_queue)?.isVisible = PlayingQueue.isNotEmpty() + return super.onPrepareOptionsMenu(menu) + } + /** * Initialize the notification badge showing the amount of new videos */ @@ -302,6 +310,13 @@ class MainActivity : BaseActivity() { startActivity(communityIntent) true } + R.id.action_queue -> { + Log.e("open queue", "open queue") + supportFragmentManager.let { + PlayingQueueSheet().show(it, null) + } + true + } else -> super.onOptionsItemSelected(item) } } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index 77aa4bf5b..af9236e46 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -1,5 +1,6 @@ package com.github.libretube.ui.adapters +import android.annotation.SuppressLint import android.text.format.DateUtils import android.view.LayoutInflater import android.view.ViewGroup @@ -24,15 +25,15 @@ class PlayingQueueAdapter : RecyclerView.Adapter() { return PlayingQueue.size() } + @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: PlayingQueueViewHolder, position: Int) { val streamItem = PlayingQueue.getStreams()[position] holder.binding.apply { ImageHelper.loadImage(streamItem.thumbnail, thumbnail) title.text = streamItem.title - uploader.text = streamItem.uploaderName - duration.text = streamItem.duration?.let { - DateUtils.formatElapsedTime(it) - } + videoInfo.text = streamItem.uploaderName + " • " + + DateUtils.formatElapsedTime(streamItem.duration ?: 0) + if (PlayingQueue.currentIndex() == position) { root.setBackgroundColor( ThemeHelper.getThemeColor(root.context, android.R.attr.colorControlHighlight) diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 863cd004a..03e420218 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -33,7 +33,6 @@ object PlayingQueue { fun getNext(): String? { return try { - Log.e("new video", queue[currentIndex() + 1].toString()) queue[currentIndex() + 1].url?.toID() } catch (e: Exception) { null diff --git a/app/src/main/res/layout/queue_row.xml b/app/src/main/res/layout/queue_row.xml index 52100c7cd..29d8a2c9c 100644 --- a/app/src/main/res/layout/queue_row.xml +++ b/app/src/main/res/layout/queue_row.xml @@ -39,27 +39,14 @@ android:maxLines="1" tools:text="I am Hardstyle - Episode 111" /> - - - - - - - + android:ellipsize="end" + android:maxLines="1" + android:textSize="14sp" + tools:text="Brennan Heart" /> diff --git a/app/src/main/res/menu/action_bar.xml b/app/src/main/res/menu/action_bar.xml index 960c25e58..90488f1d8 100644 --- a/app/src/main/res/menu/action_bar.xml +++ b/app/src/main/res/menu/action_bar.xml @@ -27,4 +27,10 @@ android:title="@string/about" app:showAsAction="never" /> + + \ No newline at end of file From a4b08578799122a9640cee743911abe1b71ae07f Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:50:45 +0200 Subject: [PATCH 10/13] layout improvements --- app/src/main/res/layout/queue_row.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/queue_row.xml b/app/src/main/res/layout/queue_row.xml index 29d8a2c9c..e03c06e5f 100644 --- a/app/src/main/res/layout/queue_row.xml +++ b/app/src/main/res/layout/queue_row.xml @@ -34,9 +34,10 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="5dp" android:ellipsize="end" android:maxLines="1" + android:textSize="16sp" + android:textStyle="bold" tools:text="I am Hardstyle - Episode 111" /> From 2e4b6beb2f05f71c5ce63ed71270dd814e607d76 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 15:58:06 +0200 Subject: [PATCH 11/13] Add a toggle at the player fragment --- .../libretube/ui/activities/MainActivity.kt | 5 +-- .../libretube/ui/fragments/PlayerFragment.kt | 3 +- app/src/main/res/drawable/ic_queue.xml | 10 +++++ .../layout/exo_styled_player_control_view.xml | 9 +++- app/src/main/res/layout/fragment_player.xml | 43 +------------------ app/src/main/res/layout/queue_row.xml | 1 + 6 files changed, 23 insertions(+), 48 deletions(-) create mode 100644 app/src/main/res/drawable/ic_queue.xml diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index bdf30f72c..d1d6f1840 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -311,10 +311,7 @@ class MainActivity : BaseActivity() { true } R.id.action_queue -> { - Log.e("open queue", "open queue") - supportFragmentManager.let { - PlayingQueueSheet().show(it, null) - } + PlayingQueueSheet().show(supportFragmentManager, null) true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 398800cc0..6f470ce70 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -404,7 +404,8 @@ class PlayerFragment : BaseFragment() { toggleComments() } - binding.queueToggle.setOnClickListener { + playerBinding.queueToggle.visibility = View.VISIBLE + playerBinding.queueToggle.setOnClickListener { PlayingQueueSheet().show(childFragmentManager, null) } diff --git a/app/src/main/res/drawable/ic_queue.xml b/app/src/main/res/drawable/ic_queue.xml new file mode 100644 index 000000000..1bc9c0aad --- /dev/null +++ b/app/src/main/res/drawable/ic_queue.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/exo_styled_player_control_view.xml b/app/src/main/res/layout/exo_styled_player_control_view.xml index 6aaeb0d73..e508662f6 100644 --- a/app/src/main/res/layout/exo_styled_player_control_view.xml +++ b/app/src/main/res/layout/exo_styled_player_control_view.xml @@ -67,10 +67,17 @@ android:layout_gravity="center" android:layoutDirection="ltr"> + + diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index a0680cc65..f6326dda2 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -298,53 +298,12 @@ app:cornerRadius="11dp" /> - - - - - - - - - - - - Date: Sun, 23 Oct 2022 16:04:15 +0200 Subject: [PATCH 12/13] Add the queue to the background mode --- .../com/github/libretube/extensions/ToStreamItem.kt | 3 +-- .../com/github/libretube/services/BackgroundMode.kt | 10 ++++++---- .../github/libretube/ui/fragments/PlayerFragment.kt | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt index f58d4d298..fee0f85a9 100644 --- a/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt +++ b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt @@ -3,8 +3,7 @@ package com.github.libretube.extensions import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.Streams -fun Streams?.toStreamItem(videoId: String): StreamItem { - if (this == null) return StreamItem() +fun Streams.toStreamItem(videoId: String): StreamItem { return StreamItem( url = videoId, title = title, diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 3faab91e9..4134ab485 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -145,8 +145,6 @@ class BackgroundMode : Service() { videoId: String, seekToPosition: Long = 0 ) { - // append the video to the playing queue - PlayingQueue.add(streams.toStreamItem(videoId)) CoroutineScope(Dispatchers.IO).launch { try { streams = RetrofitInstance.api.getStreams(videoId) @@ -156,9 +154,13 @@ class BackgroundMode : Service() { // add the playlist video to the queue if (playlistId != null && PlayingQueue.isEmpty()) { - PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId)) + streams?.toStreamItem(videoId) + ?.let { PlayingQueue.insertPlaylist(playlistId!!, it) } } else { - PlayingQueue.updateCurrent(streams.toStreamItem(videoId)) + streams?.toStreamItem(videoId)?.let { PlayingQueue.updateCurrent(it) } + streams?.relatedStreams?.toTypedArray()?.let { + PlayingQueue.add(*it) + } } handler.post { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 6f470ce70..c7ea66253 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -405,7 +405,7 @@ class PlayerFragment : BaseFragment() { } playerBinding.queueToggle.visibility = View.VISIBLE - playerBinding.queueToggle.setOnClickListener { + playerBinding.queueToggle.setOnClickListener { PlayingQueueSheet().show(childFragmentManager, null) } @@ -643,7 +643,7 @@ class PlayerFragment : BaseFragment() { return@launchWhenCreated } - if (PlayingQueue.size() == 0) { + if (PlayingQueue.isEmpty()) { CoroutineScope(Dispatchers.IO).launch { if (playlistId != null) { PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId!!)) From e0e457cd6c1a4bf9bf4e65877ea33ec26ab1171b Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Oct 2022 16:06:12 +0200 Subject: [PATCH 13/13] ktlint --- .../main/java/com/github/libretube/ui/activities/MainActivity.kt | 1 - app/src/main/java/com/github/libretube/util/PlayingQueue.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index d1d6f1840..b366b2b63 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -8,7 +8,6 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt index 03e420218..887185ae8 100644 --- a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -1,6 +1,5 @@ package com.github.libretube.util -import android.util.Log import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.StreamItem import com.github.libretube.extensions.move