diff --git a/app/src/main/java/com/github/libretube/Globals.kt b/app/src/main/java/com/github/libretube/Globals.kt deleted file mode 100644 index 48fe4a64b..000000000 --- a/app/src/main/java/com/github/libretube/Globals.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.libretube - -/** - * Global variables can be stored here - */ -object Globals { - // for downloads - var IS_DOWNLOAD_RUNNING = false - - // history of played videos in the current lifecycle - val playingQueue = mutableListOf() -} diff --git a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt index 919177c4b..e4dcb9780 100644 --- a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt +++ b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt @@ -56,9 +56,9 @@ object DatabaseHelper { fun removeWatchPosition(videoId: String) { Thread { - Database.watchPositionDao().delete( - Database.watchPositionDao().findById(videoId) - ) + Database.watchPositionDao().findById(videoId)?.let { + Database.watchPositionDao().delete(it) + } }.start() } diff --git a/app/src/main/java/com/github/libretube/db/dao/WatchPositionDao.kt b/app/src/main/java/com/github/libretube/db/dao/WatchPositionDao.kt index 09ec247ac..6855eaab4 100644 --- a/app/src/main/java/com/github/libretube/db/dao/WatchPositionDao.kt +++ b/app/src/main/java/com/github/libretube/db/dao/WatchPositionDao.kt @@ -13,7 +13,7 @@ interface WatchPositionDao { fun getAll(): List @Query("SELECT * FROM watchPosition WHERE videoId LIKE :videoId LIMIT 1") - fun findById(videoId: String): WatchPosition + fun findById(videoId: String): WatchPosition? @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertAll(vararg watchPositions: WatchPosition) diff --git a/app/src/main/java/com/github/libretube/extensions/SetWatchProgressLength.kt b/app/src/main/java/com/github/libretube/extensions/SetWatchProgressLength.kt index 0bd7d4f54..6eb979353 100644 --- a/app/src/main/java/com/github/libretube/extensions/SetWatchProgressLength.kt +++ b/app/src/main/java/com/github/libretube/extensions/SetWatchProgressLength.kt @@ -14,7 +14,7 @@ fun View?.setWatchProgressLength(videoId: String, duration: Long) { Thread { try { - progress = Database.watchPositionDao().findById(videoId).position + progress = Database.watchPositionDao().findById(videoId)?.position } catch (e: Exception) { progress = null } diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt index 1c534c8c6..858bca57d 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt @@ -35,7 +35,6 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import com.fasterxml.jackson.databind.ObjectMapper -import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.activities.MainActivity import com.github.libretube.adapters.ChaptersAdapter @@ -67,12 +66,14 @@ import com.github.libretube.obj.Segment import com.github.libretube.obj.Segments import com.github.libretube.obj.Streams import com.github.libretube.services.BackgroundMode +import com.github.libretube.services.DownloadService import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.ImageHelper import com.github.libretube.util.NetworkHelper import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper +import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.google.android.exoplayer2.C import com.google.android.exoplayer2.DefaultLoadControl @@ -210,7 +211,7 @@ class PlayerFragment : BaseFragment() { context?.hideKeyboard(view) // clear the playing queue - Globals.playingQueue.clear() + PlayingQueue.clear() setUserPrefs() @@ -686,6 +687,9 @@ class PlayerFragment : BaseFragment() { override fun onDestroy() { super.onDestroy() try { + // clear the playing queue + PlayingQueue.clear() + saveWatchPosition() nowPlayingNotification.destroy() activity?.requestedOrientation = @@ -695,6 +699,7 @@ class PlayerFragment : BaseFragment() { ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT } } catch (e: Exception) { + e.printStackTrace() } } @@ -752,8 +757,9 @@ class PlayerFragment : BaseFragment() { } private fun playVideo() { - Globals.playingQueue += videoId!! lifecycleScope.launchWhenCreated { + PlayingQueue.updateCurrent(videoId!!) + streams = try { RetrofitInstance.api.getStreams(videoId!!) } catch (e: IOException) { @@ -861,7 +867,7 @@ class PlayerFragment : BaseFragment() { var position: Long? = null Thread { try { - position = Database.watchPositionDao().findById(videoId!!).position + position = Database.watchPositionDao().findById(videoId!!)?.position // position is almost the end of the video => don't seek, start from beginning if (position!! > streams.duration!! * 1000 * 0.9) position = null } catch (e: Exception) { @@ -875,7 +881,7 @@ class PlayerFragment : BaseFragment() { private fun playNextVideo() { if (nextStreamId == null) return // check whether there is a new video in the queue - val nextQueueVideo = autoPlayHelper.getNextPlayingQueueVideoId(videoId!!) + val nextQueueVideo = PlayingQueue.getNext() if (nextQueueVideo != null) nextStreamId = nextQueueVideo // by making sure that the next and the current video aren't the same saveWatchPosition() @@ -1023,7 +1029,7 @@ class PlayerFragment : BaseFragment() { if (response.duration > 0) { // download clicked binding.relPlayerDownload.setOnClickListener { - if (!Globals.IS_DOWNLOAD_RUNNING) { + if (!DownloadService.IS_DOWNLOAD_RUNNING) { val newFragment = DownloadDialog(videoId!!) newFragment.show(childFragmentManager, DownloadDialog::class.java.name) } else { @@ -1105,7 +1111,7 @@ class PlayerFragment : BaseFragment() { // next and previous buttons playerBinding.skipPrev.visibility = if ( - skipButtonsEnabled && Globals.playingQueue.indexOf(videoId!!) != 0 + skipButtonsEnabled && PlayingQueue.hasPrev() ) { View.VISIBLE } else { @@ -1114,8 +1120,7 @@ class PlayerFragment : BaseFragment() { playerBinding.skipNext.visibility = if (skipButtonsEnabled) View.VISIBLE else View.INVISIBLE playerBinding.skipPrev.setOnClickListener { - val index = Globals.playingQueue.indexOf(videoId!!) - 1 - videoId = Globals.playingQueue[index] + videoId = PlayingQueue.getPrev() playVideo() } 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 6c75c5f21..3fd4010b2 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -11,7 +11,6 @@ import android.os.IBinder import android.os.Looper import android.widget.Toast import com.fasterxml.jackson.databind.ObjectMapper -import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.api.RetrofitInstance import com.github.libretube.constants.BACKGROUND_CHANNEL_ID @@ -25,6 +24,7 @@ import com.github.libretube.obj.Streams import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper +import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlayer @@ -119,7 +119,7 @@ class BackgroundMode : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { try { // clear the playing queue - Globals.playingQueue.clear() + PlayingQueue.clear() // get the intent arguments videoId = intent?.getStringExtra(IntentData.videoId)!! @@ -145,7 +145,7 @@ class BackgroundMode : Service() { seekToPosition: Long = 0 ) { // append the video to the playing queue - Globals.playingQueue += videoId + PlayingQueue.add(videoId) CoroutineScope(Dispatchers.IO).launch { try { streams = RetrofitInstance.api.getStreams(videoId) @@ -162,6 +162,8 @@ class BackgroundMode : Service() { private fun playAudio( seekToPosition: Long ) { + PlayingQueue.updateCurrent(videoId) + initializePlayer() setMediaItem() @@ -247,7 +249,7 @@ class BackgroundMode : Service() { */ private fun playNextVideo() { if (nextStreamId == null || nextStreamId == videoId) return - val nextQueueVideo = autoPlayHelper.getNextPlayingQueueVideoId(videoId) + val nextQueueVideo = PlayingQueue.getNext() if (nextQueueVideo != null) nextStreamId = nextQueueVideo // play new video on background @@ -331,6 +333,9 @@ class BackgroundMode : Service() { * destroy the [BackgroundMode] foreground service */ override fun onDestroy() { + // clear the playing queue + PlayingQueue.clear() + // called when the user pressed stop in the notification // stop the service from being in the foreground and remove the notification if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index 09c676c0b..bc52b3532 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -11,7 +11,6 @@ import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.constants.DOWNLOAD_CHANNEL_ID import com.github.libretube.constants.DOWNLOAD_FAILURE_NOTIFICATION_ID @@ -34,7 +33,7 @@ class DownloadService : Service() { override fun onCreate() { super.onCreate() - Globals.IS_DOWNLOAD_RUNNING = true + IS_DOWNLOAD_RUNNING = true } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -175,11 +174,16 @@ class DownloadService : Service() { try { unregisterReceiver(onDownloadComplete) } catch (e: Exception) { + e.printStackTrace() } - Globals.IS_DOWNLOAD_RUNNING = false + IS_DOWNLOAD_RUNNING = false stopService(Intent(this, DownloadService::class.java)) super.onDestroy() } + + companion object { + var IS_DOWNLOAD_RUNNING = false + } } diff --git a/app/src/main/java/com/github/libretube/sheets/VideoOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/sheets/VideoOptionsBottomSheet.kt index 0a9180ec0..ba4fa68af 100644 --- a/app/src/main/java/com/github/libretube/sheets/VideoOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/sheets/VideoOptionsBottomSheet.kt @@ -1,18 +1,14 @@ package com.github.libretube.sheets -import android.app.NotificationManager -import android.content.Context -import android.os.Build import android.os.Bundle import android.widget.Toast -import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.constants.IntentData -import com.github.libretube.constants.PLAYER_NOTIFICATION_ID import com.github.libretube.dialogs.AddToPlaylistDialog import com.github.libretube.dialogs.DownloadDialog import com.github.libretube.dialogs.ShareDialog import com.github.libretube.util.BackgroundHelper +import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PreferenceHelper import com.github.libretube.views.BottomSheet @@ -41,21 +37,13 @@ class VideoOptionsBottomSheet( ) } + /** - * Check whether the player is running by observing the notification + * Check whether the player is running and add queue options */ - try { - val notificationManager = - context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - notificationManager.activeNotifications.forEach { - if (it.id == PLAYER_NOTIFICATION_ID) { - optionsList += context?.getString(R.string.add_to_queue)!! - } - } - } - } catch (e: Exception) { - e.printStackTrace() + if (PlayingQueue.isNotEmpty()) { + optionsList += context?.getString(R.string.play_next)!! + optionsList += context?.getString(R.string.add_to_queue)!! } setSimpleItems(optionsList) { which -> @@ -89,8 +77,11 @@ class VideoOptionsBottomSheet( // using parentFragmentManager is important here shareDialog.show(parentFragmentManager, ShareDialog::class.java.name) } + context?.getString(R.string.play_next) -> { + PlayingQueue.playNext(videoId) + } context?.getString(R.string.add_to_queue) -> { - Globals.playingQueue += videoId + PlayingQueue.add(videoId) } } } 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 3217312e4..af2084374 100644 --- a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt +++ b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt @@ -1,6 +1,5 @@ package com.github.libretube.util -import com.github.libretube.Globals import com.github.libretube.api.RetrofitInstance import com.github.libretube.extensions.toID import com.github.libretube.obj.StreamItem @@ -21,12 +20,8 @@ class AutoPlayHelper( currentVideoId: String, relatedStreams: List? ): String? { - return if (Globals.playingQueue.last() != currentVideoId) { - val currentVideoIndex = Globals.playingQueue.indexOf(currentVideoId) - Globals.playingQueue[currentVideoIndex + 1] - } else if (playlistId == null) { + return if (playlistId == null) { getNextTrendingVideoId( - currentVideoId, relatedStreams ) } else { @@ -40,21 +35,13 @@ class AutoPlayHelper( * get the id of the next related video */ private fun getNextTrendingVideoId( - videoId: String, 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 || - ( - Globals.playingQueue.contains(nextStreamId) && - Globals.playingQueue.indexOf(videoId) > Globals.playingQueue.indexOf( - nextStreamId - ) - ) - ) { + while (nextStreamId == null || PlayingQueue.containsBefore(nextStreamId)) { nextStreamId = relatedStreams[index].url!!.toID() if (index + 1 < relatedStreams.size) { index += 1 @@ -103,18 +90,4 @@ class AutoPlayHelper( // return null when no nextPage is found return null } - - /** - * get the videoId of the next video in the playing queue - */ - fun getNextPlayingQueueVideoId( - currentVideoId: String - ): String? { - return if (Globals.playingQueue.last() != currentVideoId) { - val currentVideoIndex = Globals.playingQueue.indexOf(currentVideoId) - Globals.playingQueue[currentVideoIndex + 1] - } else { - null - } - } } diff --git a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt index b50df7208..25aec4703 100644 --- a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt +++ b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt @@ -166,6 +166,7 @@ class NowPlayingNotification( Context.NOTIFICATION_SERVICE ) as NotificationManager notificationManager.cancel(PLAYER_NOTIFICATION_ID) + player.stop() player.release() } } diff --git a/app/src/main/java/com/github/libretube/util/PlayingQueue.kt b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt new file mode 100644 index 000000000..651ff963c --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/PlayingQueue.kt @@ -0,0 +1,58 @@ +package com.github.libretube.util + +object PlayingQueue { + private val queue = mutableListOf() + private var currentVideoId: String? = null + + fun clear() { + queue.clear() + } + + fun add(videoId: String) { + if (currentVideoId == videoId) return + if (queue.contains(videoId)) queue.remove(videoId) + queue.add(videoId) + } + + fun playNext(videoId: String) { + if (currentVideoId == videoId) return + if (queue.contains(videoId)) queue.remove(videoId) + queue.add( + queue.indexOf(currentVideoId) + 1, + videoId + ) + } + + fun getNext(): String? { + val currentIndex = queue.indexOf(currentVideoId) + return if (currentIndex >= queue.size) { + null + } else { + queue[currentIndex + 1] + } + } + + fun getPrev(): String? { + val index = queue.indexOf(currentVideoId) + return if (index > 0) queue[index - 1] else null + } + + fun hasPrev(): Boolean { + return queue.indexOf(currentVideoId) > 0 + } + + fun contains(videoId: String): Boolean { + return queue.contains(videoId) + } + + fun containsBefore(videoId: String): Boolean { + return queue.contains(videoId) && queue.indexOf(videoId) < queue.indexOf(currentVideoId) + } + + fun updateCurrent(videoId: String) { + currentVideoId = videoId + queue.add(videoId) + } + + fun isNotEmpty() = queue.isNotEmpty() +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4cfdce884..b49c26d4e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,6 +329,7 @@ Custom Instances Load feed in background Load the subscription feed in the background and prevent it from being auto-refreshed. + Play next Download Service