Merge pull request #2694 from Bnyro/master

Add audio only mode
This commit is contained in:
Bnyro 2023-01-14 16:59:43 +01:00 committed by GitHub
commit 0357b736a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 42 deletions

View File

@ -93,6 +93,7 @@ object PreferenceKeys {
* Background mode * Background mode
*/ */
const val BACKGROUND_PLAYBACK_SPEED = "background_playback_speed" const val BACKGROUND_PLAYBACK_SPEED = "background_playback_speed"
const val AUDIO_ONLY_MODE = "audio_only_mode"
/** /**
* Notifications * Notifications

View File

@ -10,6 +10,7 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper import android.os.Looper
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -24,6 +25,7 @@ import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.WatchPosition import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
@ -127,7 +129,7 @@ class BackgroundMode : Service() {
*/ */
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try { try {
// clear the playing queue // reset the playing queue listeners
PlayingQueue.resetToDefaults() PlayingQueue.resetToDefaults()
// get the intent arguments // get the intent arguments
@ -145,6 +147,7 @@ class BackgroundMode : Service() {
if (PlayerHelper.watchPositionsEnabled) updateWatchPosition() if (PlayerHelper.watchPositionsEnabled) updateWatchPosition()
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), e.toString())
onDestroy() onDestroy()
} }
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
@ -177,23 +180,23 @@ class BackgroundMode : Service() {
RetrofitInstance.api.getStreams(videoId) RetrofitInstance.api.getStreams(videoId)
}.getOrNull() ?: return@launch }.getOrNull() ?: return@launch
// clear the queue if it shouldn't be kept explicitly
if (!keepQueue) PlayingQueue.clear()
if (PlayingQueue.isEmpty()) updateQueue()
// save the current stream to the queue // save the current stream to the queue
streams?.toStreamItem(videoId)?.let { streams?.toStreamItem(videoId)?.let {
PlayingQueue.updateCurrent(it) PlayingQueue.updateCurrent(it)
} }
// add the playlist video to the queue
if (PlayingQueue.isEmpty() && !keepQueue) updateQueue()
handler.post { handler.post {
playAudio(seekToPosition) playAudio(seekToPosition)
} }
} }
} }
private fun playAudio( private fun playAudio(seekToPosition: Long) {
seekToPosition: Long
) {
initializePlayer() initializePlayer()
setMediaItem() setMediaItem()

View File

@ -41,6 +41,7 @@ import com.github.libretube.ui.models.SearchViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.tools.BreakReminder import com.github.libretube.ui.tools.BreakReminder
import com.github.libretube.util.NavBarHelper import com.github.libretube.util.NavBarHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.NetworkHelper import com.github.libretube.util.NetworkHelper
import com.github.libretube.util.PreferenceHelper import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.ThemeHelper import com.github.libretube.util.ThemeHelper
@ -58,6 +59,8 @@ class MainActivity : BaseActivity() {
lateinit var searchView: SearchView lateinit var searchView: SearchView
private lateinit var searchItem: MenuItem private lateinit var searchItem: MenuItem
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -381,7 +384,7 @@ class MainActivity : BaseActivity() {
} }
if (intent?.getBooleanExtra(IntentData.openAudioPlayer, false) == true) { if (intent?.getBooleanExtra(IntentData.openAudioPlayer, false) == true) {
navController.navigate(R.id.audioPlayerFragment) NavigationHelper.startAudioPlayer(this)
return return
} }
@ -404,7 +407,11 @@ class MainActivity : BaseActivity() {
) )
} }
intent?.getStringExtra(IntentData.videoId)?.let { intent?.getStringExtra(IntentData.videoId)?.let {
loadVideo(it, intent?.getLongExtra(IntentData.timeStamp, 0L)) NavigationHelper.navigateVideo(
context = this,
videoId = it,
timeStamp = intent?.getLongExtra(IntentData.timeStamp, 0L)
)
} }
when (intent?.getStringExtra("fragmentToOpen")) { when (intent?.getStringExtra("fragmentToOpen")) {
@ -419,32 +426,6 @@ class MainActivity : BaseActivity() {
} }
} }
private fun loadVideo(videoId: String, timeStamp: Long?) {
val bundle = Bundle()
bundle.putString(IntentData.videoId, videoId)
if (timeStamp != null) bundle.putLong(IntentData.timeStamp, timeStamp)
val frag = PlayerFragment()
frag.arguments = bundle
supportFragmentManager.beginTransaction()
.remove(PlayerFragment())
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler(Looper.getMainLooper()).postDelayed({
supportFragmentManager.fragments.forEach { fragment ->
(fragment as? PlayerFragment)
?.binding?.playerMotionLayout?.apply {
transitionToEnd()
transitionToStart()
}
}
}, 300)
}
private fun minimizePlayer() { private fun minimizePlayer() {
binding.mainMotionLayout.transitionToEnd() binding.mainMotionLayout.transitionToEnd()
supportFragmentManager.fragments.forEach { fragment -> supportFragmentManager.fragments.forEach { fragment ->

View File

@ -86,6 +86,7 @@ import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.DashHelper import com.github.libretube.util.DashHelper
import com.github.libretube.util.DataSaverMode import com.github.libretube.util.DataSaverMode
import com.github.libretube.util.ImageHelper import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayerHelper import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
@ -349,7 +350,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
BackgroundHelper.stopBackgroundPlay(requireContext()) BackgroundHelper.stopBackgroundPlay(requireContext())
} }
playerBinding.closeImageButton.setOnClickListener { playerBinding.closeImageButton.setOnClickListener {
killFragment() killPlayerFragment()
} }
playerBinding.autoPlay.visibility = View.VISIBLE playerBinding.autoPlay.visibility = View.VISIBLE
@ -476,8 +477,8 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
true true
) )
handler.postDelayed({ handler.postDelayed({
(activity as MainActivity).navController.navigate(R.id.audioPlayerFragment) NavigationHelper.startAudioPlayer(requireContext())
killFragment() killPlayerFragment()
}, 500) }, 500)
} }
@ -1530,7 +1531,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
return exoPlayer.isPlaying && !backgroundModeRunning return exoPlayer.isPlaying && !backgroundModeRunning
} }
private fun killFragment() { private fun killPlayerFragment() {
viewModel.isFullscreen.value = false viewModel.isFullscreen.value = false
binding.playerMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd()
val mainActivity = activity as MainActivity val mainActivity = activity as MainActivity

View File

@ -6,10 +6,13 @@ import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
@ -17,6 +20,8 @@ import com.github.libretube.ui.fragments.PlayerFragment
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
object NavigationHelper { object NavigationHelper {
private val handler = Handler(Looper.getMainLooper())
fun navigateChannel( fun navigateChannel(
context: Context, context: Context,
channelId: String? channelId: String?
@ -45,20 +50,35 @@ object NavigationHelper {
return correctContext as MainActivity return correctContext as MainActivity
} }
/**
* Navigate to the given video using the other provided parameters as well
* If the audio only mode is enabled, play it in the background, else as a normal video
*/
fun navigateVideo( fun navigateVideo(
context: Context, context: Context,
videoId: String?, videoId: String?,
playlistId: String? = null, playlistId: String? = null,
channelId: String? = null, channelId: String? = null,
keepQueue: Boolean = false keepQueue: Boolean = false,
timeStamp: Long? = null
) { ) {
if (videoId == null) return if (videoId == null) return
if (PreferenceHelper.getBoolean(PreferenceKeys.AUDIO_ONLY_MODE, false)) {
BackgroundHelper.stopBackgroundPlay(context)
BackgroundHelper.playOnBackground(context, videoId.toID(), timeStamp, playlistId, channelId, keepQueue)
handler.postDelayed({
startAudioPlayer(context)
}, 500)
return
}
val bundle = Bundle().apply { val bundle = Bundle().apply {
putString(IntentData.videoId, videoId.toID()) putString(IntentData.videoId, videoId.toID())
putString(IntentData.playlistId, playlistId) putString(IntentData.playlistId, playlistId)
putString(IntentData.channelId, channelId) putString(IntentData.channelId, channelId)
putBoolean(IntentData.keepQueue, keepQueue) putBoolean(IntentData.keepQueue, keepQueue)
timeStamp?.let { putLong(IntentData.timeStamp, it) }
} }
val activity = context as AppCompatActivity val activity = context as AppCompatActivity
@ -89,6 +109,14 @@ object NavigationHelper {
activity.navController.navigate(R.id.playlistFragment, bundle) activity.navController.navigate(R.id.playlistFragment, bundle)
} }
/**
* Start the audio player fragment
*/
fun startAudioPlayer(context: Context) {
val activity = unwrap(context)
activity.navController.navigate(R.id.audioPlayerFragment)
}
/** /**
* Needed due to different MainActivity Aliases because of the app icons * Needed due to different MainActivity Aliases because of the app icons
*/ */

View File

@ -25,6 +25,8 @@ object PlayingQueue {
private val onTrackChangedListeners: MutableList<(StreamItem) -> Unit> = mutableListOf() private val onTrackChangedListeners: MutableList<(StreamItem) -> Unit> = mutableListOf()
var repeatQueue: Boolean = false var repeatQueue: Boolean = false
fun clear() = queue.clear()
fun add(vararg streamItem: StreamItem) { fun add(vararg streamItem: StreamItem) {
streamItem.forEach { streamItem.forEach {
if (currentStream != it) { if (currentStream != it) {
@ -72,7 +74,7 @@ object PlayingQueue {
it.invoke(streamItem) it.invoke(streamItem)
} }
} }
if (!contains(streamItem)) queue.add(streamItem) if (!contains(streamItem)) queue.add(0, streamItem)
} }
fun isNotEmpty() = queue.isNotEmpty() fun isNotEmpty() = queue.isNotEmpty()
@ -190,6 +192,5 @@ object PlayingQueue {
repeatQueue = false repeatQueue = false
onQueueTapListener = {} onQueueTapListener = {}
onTrackChangedListeners.clear() onTrackChangedListeners.clear()
queue.clear()
} }
} }

View File

@ -431,6 +431,8 @@
<string name="alternative_pip_controls">Alternative PiP controls</string> <string name="alternative_pip_controls">Alternative PiP controls</string>
<string name="alternative_pip_controls_summary">Show audio only and skip controls in PiP instead of forward and rewind</string> <string name="alternative_pip_controls_summary">Show audio only and skip controls in PiP instead of forward and rewind</string>
<string name="audio_player">Audio player</string> <string name="audio_player">Audio player</string>
<string name="audio_only_mode">Audio only mode</string>
<string name="audio_only_mode_summary">Turn LibreTube into a music player.</string>
<!-- Notification channel strings --> <!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string> <string name="download_channel_name">Download Service</string>

View File

@ -23,6 +23,13 @@
<PreferenceCategory app:title="@string/customization"> <PreferenceCategory app:title="@string/customization">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_headphones"
android:summary="@string/audio_only_mode_summary"
android:title="@string/audio_only_mode"
app:key="audio_only_mode" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="false" android:defaultValue="false"
android:icon="@drawable/ic_screen_rotation" android:icon="@drawable/ic_screen_rotation"