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