mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 14:20:30 +05:30
Merge pull request #6641 from Bnyro/master
feat: audio player support for no internet activity
This commit is contained in:
commit
a50b2f75e4
@ -43,6 +43,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.activities.NoInternetActivity"
|
android:name=".ui.activities.NoInternetActivity"
|
||||||
android:label="@string/noInternet"
|
android:label="@string/noInternet"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="locked" />
|
android:screenOrientation="locked" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@ -114,6 +115,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.activities.OfflinePlayerActivity"
|
android:name=".ui.activities.OfflinePlayerActivity"
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:label="@string/player"
|
android:label="@string/player"
|
||||||
android:supportsPictureInPicture="true" />
|
android:supportsPictureInPicture="true" />
|
||||||
|
|
||||||
@ -410,7 +412,8 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="mediaPlayback" />
|
android:foregroundServiceType="mediaPlayback" />
|
||||||
|
|
||||||
<service android:name=".services.OnClearFromRecentService"
|
<service
|
||||||
|
android:name=".services.OnClearFromRecentService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
@ -51,4 +51,5 @@ object IntentData {
|
|||||||
const val offlinePlayer = "offlinePlayer"
|
const val offlinePlayer = "offlinePlayer"
|
||||||
const val downloadTab = "downloadTab"
|
const val downloadTab = "downloadTab"
|
||||||
const val shuffle = "shuffle"
|
const val shuffle = "shuffle"
|
||||||
|
const val noInternet = "noInternet"
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import com.github.libretube.parcelable.PlayerData
|
|||||||
import com.github.libretube.services.OfflinePlayerService
|
import com.github.libretube.services.OfflinePlayerService
|
||||||
import com.github.libretube.services.OnlinePlayerService
|
import com.github.libretube.services.OnlinePlayerService
|
||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
|
import com.github.libretube.ui.activities.NoInternetActivity
|
||||||
import com.github.libretube.ui.fragments.DownloadTab
|
import com.github.libretube.ui.fragments.DownloadTab
|
||||||
import com.github.libretube.ui.fragments.PlayerFragment
|
import com.github.libretube.ui.fragments.PlayerFragment
|
||||||
|
|
||||||
@ -80,10 +81,14 @@ object BackgroundHelper {
|
|||||||
fun playOnBackgroundOffline(context: Context, videoId: String?, downloadTab: DownloadTab, shuffle: Boolean = false) {
|
fun playOnBackgroundOffline(context: Context, videoId: String?, downloadTab: DownloadTab, shuffle: Boolean = false) {
|
||||||
stopBackgroundPlay(context)
|
stopBackgroundPlay(context)
|
||||||
|
|
||||||
|
// whether the service is started from the MainActivity or NoInternetActivity
|
||||||
|
val noInternet = ContextHelper.tryUnwrapActivity<NoInternetActivity>(context) != null
|
||||||
|
|
||||||
val playerIntent = Intent(context, OfflinePlayerService::class.java)
|
val playerIntent = Intent(context, OfflinePlayerService::class.java)
|
||||||
.putExtra(IntentData.videoId, videoId)
|
.putExtra(IntentData.videoId, videoId)
|
||||||
.putExtra(IntentData.shuffle, shuffle)
|
.putExtra(IntentData.shuffle, shuffle)
|
||||||
.putExtra(IntentData.downloadTab, downloadTab)
|
.putExtra(IntentData.downloadTab, downloadTab)
|
||||||
|
.putExtra(IntentData.noInternet, noInternet)
|
||||||
|
|
||||||
ContextCompat.startForegroundService(context, playerIntent)
|
ContextCompat.startForegroundService(context, playerIntent)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import com.github.libretube.extensions.toID
|
|||||||
import com.github.libretube.parcelable.PlayerData
|
import com.github.libretube.parcelable.PlayerData
|
||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
import com.github.libretube.ui.activities.ZoomableImageActivity
|
import com.github.libretube.ui.activities.ZoomableImageActivity
|
||||||
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
import com.github.libretube.ui.fragments.AudioPlayerFragment
|
import com.github.libretube.ui.fragments.AudioPlayerFragment
|
||||||
import com.github.libretube.ui.fragments.PlayerFragment
|
import com.github.libretube.ui.fragments.PlayerFragment
|
||||||
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
|
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
|
||||||
@ -98,7 +99,7 @@ object NavigationHelper {
|
|||||||
* Start the audio player fragment
|
* Start the audio player fragment
|
||||||
*/
|
*/
|
||||||
fun startAudioPlayer(context: Context, offlinePlayer: Boolean = false, minimizeByDefault: Boolean = false) {
|
fun startAudioPlayer(context: Context, offlinePlayer: Boolean = false, minimizeByDefault: Boolean = false) {
|
||||||
val activity = ContextHelper.unwrapActivity<MainActivity>(context)
|
val activity = ContextHelper.unwrapActivity<BaseActivity>(context)
|
||||||
activity.supportFragmentManager.commitNow {
|
activity.supportFragmentManager.commitNow {
|
||||||
val args = bundleOf(
|
val args = bundleOf(
|
||||||
IntentData.minimizeByDefault to minimizeByDefault,
|
IntentData.minimizeByDefault to minimizeByDefault,
|
||||||
|
@ -124,6 +124,7 @@ abstract class AbstractPlayerService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract val isOfflinePlayer: Boolean
|
abstract val isOfflinePlayer: Boolean
|
||||||
|
abstract val intentActivity: Class<*>
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@ -149,8 +150,8 @@ abstract class AbstractPlayerService : LifecycleService() {
|
|||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
createPlayerAndNotification()
|
|
||||||
onServiceCreated(intent)
|
onServiceCreated(intent)
|
||||||
|
createPlayerAndNotification()
|
||||||
startPlaybackAndUpdateNotification()
|
startPlaybackAndUpdateNotification()
|
||||||
}
|
}
|
||||||
else stopSelf()
|
else stopSelf()
|
||||||
@ -181,7 +182,8 @@ abstract class AbstractPlayerService : LifecycleService() {
|
|||||||
this,
|
this,
|
||||||
player!!,
|
player!!,
|
||||||
backgroundOnly = true,
|
backgroundOnly = true,
|
||||||
offlinePlayer = isOfflinePlayer
|
offlinePlayer = isOfflinePlayer,
|
||||||
|
intentActivity = intentActivity
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ import com.github.libretube.extensions.toAndroidUri
|
|||||||
import com.github.libretube.extensions.toID
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
import com.github.libretube.obj.PlayerNotificationData
|
import com.github.libretube.obj.PlayerNotificationData
|
||||||
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
|
import com.github.libretube.ui.activities.NoInternetActivity
|
||||||
import com.github.libretube.ui.fragments.DownloadTab
|
import com.github.libretube.ui.fragments.DownloadTab
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -31,6 +33,9 @@ import kotlin.io.path.exists
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
class OfflinePlayerService : AbstractPlayerService() {
|
class OfflinePlayerService : AbstractPlayerService() {
|
||||||
override val isOfflinePlayer: Boolean = true
|
override val isOfflinePlayer: Boolean = true
|
||||||
|
private var noInternetService: Boolean = false
|
||||||
|
override val intentActivity: Class<*>
|
||||||
|
get() = if (noInternetService) NoInternetActivity::class.java else MainActivity::class.java
|
||||||
|
|
||||||
private var downloadWithItems: DownloadWithItems? = null
|
private var downloadWithItems: DownloadWithItems? = null
|
||||||
private lateinit var downloadTab: DownloadTab
|
private lateinit var downloadTab: DownloadTab
|
||||||
@ -39,6 +44,7 @@ class OfflinePlayerService : AbstractPlayerService() {
|
|||||||
override suspend fun onServiceCreated(intent: Intent) {
|
override suspend fun onServiceCreated(intent: Intent) {
|
||||||
downloadTab = intent.serializableExtra(IntentData.downloadTab)!!
|
downloadTab = intent.serializableExtra(IntentData.downloadTab)!!
|
||||||
shuffle = intent.getBooleanExtra(IntentData.shuffle, false)
|
shuffle = intent.getBooleanExtra(IntentData.shuffle, false)
|
||||||
|
noInternetService = intent.getBooleanExtra(IntentData.noInternet, false)
|
||||||
|
|
||||||
videoId = if (shuffle) {
|
videoId = if (shuffle) {
|
||||||
runBlocking(Dispatchers.IO) {
|
runBlocking(Dispatchers.IO) {
|
||||||
|
@ -23,6 +23,7 @@ import com.github.libretube.helpers.PlayerHelper.checkForSegments
|
|||||||
import com.github.libretube.helpers.ProxyHelper
|
import com.github.libretube.helpers.ProxyHelper
|
||||||
import com.github.libretube.obj.PlayerNotificationData
|
import com.github.libretube.obj.PlayerNotificationData
|
||||||
import com.github.libretube.parcelable.PlayerData
|
import com.github.libretube.parcelable.PlayerData
|
||||||
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -35,6 +36,7 @@ import kotlinx.serialization.encodeToString
|
|||||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
class OnlinePlayerService : AbstractPlayerService() {
|
class OnlinePlayerService : AbstractPlayerService() {
|
||||||
override val isOfflinePlayer: Boolean = false
|
override val isOfflinePlayer: Boolean = false
|
||||||
|
override val intentActivity: Class<*> = MainActivity::class.java
|
||||||
|
|
||||||
// PlaylistId/ChannelId for autoplay
|
// PlaylistId/ChannelId for autoplay
|
||||||
private var playlistId: String? = null
|
private var playlistId: String? = null
|
||||||
|
@ -6,10 +6,12 @@ import androidx.activity.addCallback
|
|||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
import androidx.fragment.app.replace
|
import androidx.fragment.app.replace
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.constants.IntentData
|
||||||
import com.github.libretube.databinding.ActivityNointernetBinding
|
import com.github.libretube.databinding.ActivityNointernetBinding
|
||||||
import com.github.libretube.helpers.NavigationHelper
|
import com.github.libretube.helpers.NavigationHelper
|
||||||
import com.github.libretube.helpers.NetworkHelper
|
import com.github.libretube.helpers.NetworkHelper
|
||||||
import com.github.libretube.ui.base.BaseActivity
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
|
import com.github.libretube.ui.fragments.AudioPlayerFragment
|
||||||
import com.github.libretube.ui.fragments.DownloadsFragment
|
import com.github.libretube.ui.fragments.DownloadsFragment
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ class NoInternetActivity : BaseActivity() {
|
|||||||
|
|
||||||
binding.downloads.setOnClickListener {
|
binding.downloads.setOnClickListener {
|
||||||
supportFragmentManager.commit {
|
supportFragmentManager.commit {
|
||||||
replace<DownloadsFragment>(R.id.noInternet_container)
|
replace<DownloadsFragment>(R.id.container)
|
||||||
addToBackStack(null)
|
addToBackStack(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,4 +53,17 @@ class NoInternetActivity : BaseActivity() {
|
|||||||
?: finishAffinity()
|
?: finishAffinity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
|
if (intent.getBooleanExtra(IntentData.openAudioPlayer, false)) {
|
||||||
|
// attempt to recycle already existing audio player fragment first before creating new one
|
||||||
|
supportFragmentManager.fragments.filterIsInstance<AudioPlayerFragment>().firstOrNull()?.let {
|
||||||
|
it.binding.playerMotionLayout.transitionToStart()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NavigationHelper.startAudioPlayer(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,8 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
nowPlayingNotification = NowPlayingNotification(
|
nowPlayingNotification = NowPlayingNotification(
|
||||||
this,
|
this,
|
||||||
viewModel.player,
|
viewModel.player,
|
||||||
offlinePlayer = true
|
offlinePlayer = true,
|
||||||
|
intentActivity = OfflinePlayerActivity::class.java
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ import com.github.libretube.services.AbstractPlayerService
|
|||||||
import com.github.libretube.services.OfflinePlayerService
|
import com.github.libretube.services.OfflinePlayerService
|
||||||
import com.github.libretube.services.OnlinePlayerService
|
import com.github.libretube.services.OnlinePlayerService
|
||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
import com.github.libretube.ui.interfaces.AudioPlayerOptions
|
import com.github.libretube.ui.interfaces.AudioPlayerOptions
|
||||||
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
|
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
|
||||||
import com.github.libretube.ui.models.ChaptersViewModel
|
import com.github.libretube.ui.models.ChaptersViewModel
|
||||||
@ -64,7 +65,8 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var audioHelper: AudioHelper
|
private lateinit var audioHelper: AudioHelper
|
||||||
private val mainActivity get() = context as MainActivity
|
private val activity get() = context as BaseActivity
|
||||||
|
private val mainActivity get() = activity as? MainActivity
|
||||||
private val viewModel: CommonPlayerViewModel by activityViewModels()
|
private val viewModel: CommonPlayerViewModel by activityViewModels()
|
||||||
private val chaptersModel: ChaptersViewModel by activityViewModels()
|
private val chaptersModel: ChaptersViewModel by activityViewModels()
|
||||||
|
|
||||||
@ -99,7 +101,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
val serviceClass =
|
val serviceClass =
|
||||||
if (isOffline) OfflinePlayerService::class.java else OnlinePlayerService::class.java
|
if (isOffline) OfflinePlayerService::class.java else OnlinePlayerService::class.java
|
||||||
Intent(activity, serviceClass).also { intent ->
|
Intent(activity, serviceClass).also { intent ->
|
||||||
activity?.bindService(intent, connection, 0)
|
activity.bindService(intent, connection, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +134,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.minimizePlayer.setOnClickListener {
|
binding.minimizePlayer.setOnClickListener {
|
||||||
val mainMotionLayout = mainActivity.binding.mainMotionLayout
|
mainActivity?.binding?.mainMotionLayout?.transitionToStart()
|
||||||
mainMotionLayout.transitionToStart()
|
|
||||||
binding.playerMotionLayout.transitionToEnd()
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +209,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.miniPlayerClose.setOnClickListener {
|
binding.miniPlayerClose.setOnClickListener {
|
||||||
activity?.unbindService(connection)
|
activity.unbindService(connection)
|
||||||
BackgroundHelper.stopBackgroundPlay(requireContext())
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
killFragment()
|
killFragment()
|
||||||
}
|
}
|
||||||
@ -244,15 +245,17 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
private fun killFragment() {
|
private fun killFragment() {
|
||||||
viewModel.isFullscreen.value = false
|
viewModel.isFullscreen.value = false
|
||||||
binding.playerMotionLayout.transitionToEnd()
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
mainActivity.supportFragmentManager.commit {
|
activity.supportFragmentManager.commit {
|
||||||
remove(this@AudioPlayerFragment)
|
remove(this@AudioPlayerFragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun initializeTransitionLayout() {
|
private fun initializeTransitionLayout() {
|
||||||
mainActivity.binding.container.isVisible = true
|
if (mainActivity == null) return
|
||||||
val mainMotionLayout = mainActivity.binding.mainMotionLayout
|
|
||||||
|
mainActivity!!.binding.container.isVisible = true
|
||||||
|
val mainMotionLayout = mainActivity!!.binding.mainMotionLayout
|
||||||
mainMotionLayout.progress = 0F
|
mainMotionLayout.progress = 0F
|
||||||
|
|
||||||
binding.playerMotionLayout.addTransitionListener(object : TransitionAdapter() {
|
binding.playerMotionLayout.addTransitionListener(object : TransitionAdapter() {
|
||||||
@ -408,7 +411,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
// unregister all listeners and the connected [playerService]
|
// unregister all listeners and the connected [playerService]
|
||||||
playerService?.onStateOrPlayingChanged = null
|
playerService?.onStateOrPlayingChanged = null
|
||||||
runCatching {
|
runCatching {
|
||||||
activity?.unbindService(connection)
|
activity.unbindService(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
@ -35,7 +35,8 @@ class NowPlayingNotification(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val player: ExoPlayer,
|
private val player: ExoPlayer,
|
||||||
private val backgroundOnly: Boolean = false,
|
private val backgroundOnly: Boolean = false,
|
||||||
private val offlinePlayer: Boolean = false
|
private val offlinePlayer: Boolean = false,
|
||||||
|
private val intentActivity: Class<*> = MainActivity::class.java
|
||||||
) {
|
) {
|
||||||
private var videoId: String? = null
|
private var videoId: String? = null
|
||||||
private val nManager = context.getSystemService<NotificationManager>()!!
|
private val nManager = context.getSystemService<NotificationManager>()!!
|
||||||
@ -74,14 +75,14 @@ class NowPlayingNotification(
|
|||||||
// starts a new MainActivity Intent when the player notification is clicked
|
// starts a new MainActivity Intent when the player notification is clicked
|
||||||
// it doesn't start a completely new MainActivity because the MainActivity's launchMode
|
// it doesn't start a completely new MainActivity because the MainActivity's launchMode
|
||||||
// is set to "singleTop" in the AndroidManifest (important!!!)
|
// is set to "singleTop" in the AndroidManifest (important!!!)
|
||||||
// that's the only way to launch back into the previous activity (e.g. the player view
|
// that's the only way to launch back into the previous activity (e.g. the player view)
|
||||||
if (!backgroundOnly) return null
|
val intent = Intent(context, intentActivity).apply {
|
||||||
|
if (backgroundOnly) {
|
||||||
val intent = Intent(context, MainActivity::class.java).apply {
|
|
||||||
putExtra(IntentData.openAudioPlayer, true)
|
putExtra(IntentData.openAudioPlayer, true)
|
||||||
putExtra(IntentData.offlinePlayer, offlinePlayer)
|
putExtra(IntentData.offlinePlayer, offlinePlayer)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return PendingIntentCompat
|
return PendingIntentCompat
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/noInternet_container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:elevation="20dp"
|
android:elevation="20dp"
|
||||||
|
Loading…
Reference in New Issue
Block a user