mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-15 06:40:30 +05:30
Audio/background player for downloads
This commit is contained in:
parent
57b7dfdda9
commit
dcdd1af176
@ -339,7 +339,12 @@
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".services.BackgroundMode"
|
||||
android:name=".services.OnlinePlayerService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".services.OfflinePlayerService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
|
@ -7,17 +7,17 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.commit
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.services.BackgroundMode
|
||||
import com.github.libretube.services.OnlinePlayerService
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import com.github.libretube.ui.fragments.PlayerFragment
|
||||
|
||||
/**
|
||||
* Helper for starting a new Instance of the [BackgroundMode]
|
||||
* Helper for starting a new Instance of the [OnlinePlayerService]
|
||||
*/
|
||||
object BackgroundHelper {
|
||||
|
||||
/**
|
||||
* Start the foreground service [BackgroundMode] to play in background. [position]
|
||||
* Start the foreground service [OnlinePlayerService] to play in background. [position]
|
||||
* is seek to position specified in milliseconds in the current [videoId].
|
||||
*/
|
||||
fun playOnBackground(
|
||||
@ -39,7 +39,7 @@ object BackgroundHelper {
|
||||
}
|
||||
|
||||
// create an intent for the background mode service
|
||||
val intent = Intent(context, BackgroundMode::class.java)
|
||||
val intent = Intent(context, OnlinePlayerService::class.java)
|
||||
intent.putExtra(IntentData.videoId, videoId)
|
||||
intent.putExtra(IntentData.playlistId, playlistId)
|
||||
intent.putExtra(IntentData.channelId, channelId)
|
||||
@ -51,22 +51,22 @@ object BackgroundHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the [BackgroundMode] service if it is running.
|
||||
* Stop the [OnlinePlayerService] service if it is running.
|
||||
*/
|
||||
fun stopBackgroundPlay(context: Context) {
|
||||
if (isBackgroundServiceRunning(context)) {
|
||||
// Intent to stop background mode service
|
||||
val intent = Intent(context, BackgroundMode::class.java)
|
||||
val intent = Intent(context, OnlinePlayerService::class.java)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the [BackgroundMode] service is currently running.
|
||||
* Check if the [OnlinePlayerService] service is currently running.
|
||||
*/
|
||||
fun isBackgroundServiceRunning(context: Context): Boolean {
|
||||
@Suppress("DEPRECATION")
|
||||
return context.getSystemService<ActivityManager>()!!.getRunningServices(Int.MAX_VALUE)
|
||||
.any { BackgroundMode::class.java.name == it.service.className }
|
||||
.any { OnlinePlayerService::class.java.name == it.service.className }
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +93,7 @@ object ImageHelper {
|
||||
* Get a squared bitmap with the same width and height from a bitmap
|
||||
* @param bitmap The bitmap to resize
|
||||
*/
|
||||
fun getSquareBitmap(bitmap: Bitmap?): Bitmap? {
|
||||
bitmap ?: return null
|
||||
fun getSquareBitmap(bitmap: Bitmap): Bitmap {
|
||||
val newSize = minOf(bitmap.width, bitmap.height)
|
||||
return Bitmap.createBitmap(
|
||||
bitmap,
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.github.libretube.obj
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
data class PlayerNotificationData(
|
||||
val title: String? = null,
|
||||
val uploaderName: String? = null,
|
||||
val thumbnailUrl: String? = null,
|
||||
val thumbnailPath: Path? = null
|
||||
)
|
@ -0,0 +1,114 @@
|
||||
package com.github.libretube.services
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.obj.DownloadWithItems
|
||||
import com.github.libretube.enums.FileType
|
||||
import com.github.libretube.extensions.toAndroidUri
|
||||
import com.github.libretube.helpers.PlayerHelper
|
||||
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.obj.PlayerNotificationData
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* A service to play downloaded audio in the background
|
||||
*/
|
||||
class OfflinePlayerService : Service() {
|
||||
private var player: ExoPlayer? = null
|
||||
private var nowPlayingNotification: NowPlayingNotification? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
val notification = NotificationCompat.Builder(this, BACKGROUND_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(getString(R.string.playingOnBackground))
|
||||
.setSmallIcon(R.drawable.ic_launcher_lockscreen)
|
||||
.build()
|
||||
|
||||
startForeground(PLAYER_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
val videoId = intent.getStringExtra(IntentData.videoId)
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val downloadWithItems = DatabaseHolder.Database.downloadDao().findById(videoId!!)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (startAudioPlayer(downloadWithItems)) {
|
||||
nowPlayingNotification = NowPlayingNotification(
|
||||
this@OfflinePlayerService,
|
||||
player!!,
|
||||
true
|
||||
)
|
||||
val notificationData = PlayerNotificationData(
|
||||
title = downloadWithItems.download.title,
|
||||
uploaderName = downloadWithItems.download.uploader,
|
||||
thumbnailPath = downloadWithItems.download.thumbnailPath
|
||||
)
|
||||
nowPlayingNotification?.updatePlayerNotification(videoId, notificationData)
|
||||
} else {
|
||||
onDestroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to start an audio player with the given download items
|
||||
* @param downloadWithItem The database download to play from
|
||||
* @return whether starting the audio player succeeded
|
||||
*/
|
||||
private fun startAudioPlayer(downloadWithItem: DownloadWithItems): Boolean {
|
||||
player = ExoPlayer.Builder(this)
|
||||
.setUsePlatformDiagnostics(false)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setAudioAttributes(PlayerHelper.getAudioAttributes(), true)
|
||||
.setLoadControl(PlayerHelper.getLoadControl())
|
||||
.build()
|
||||
.loadPlaybackParams(isBackgroundMode = true).apply {
|
||||
playWhenReady = true
|
||||
}
|
||||
|
||||
val audioItem = downloadWithItem.downloadItems.firstOrNull { it.type == FileType.AUDIO }
|
||||
?: // in some rare cases, video files can contain audio
|
||||
downloadWithItem.downloadItems.firstOrNull { it.type == FileType.VIDEO } ?: return false
|
||||
|
||||
val mediaItem = MediaItem.Builder()
|
||||
.setUri(audioItem.path.toAndroidUri())
|
||||
.build()
|
||||
|
||||
player?.setMediaItem(mediaItem)
|
||||
player?.prepare()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
nowPlayingNotification?.destroySelfAndPlayer()
|
||||
|
||||
player = null
|
||||
nowPlayingNotification = null
|
||||
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?) = null
|
||||
}
|
@ -3,12 +3,12 @@ package com.github.libretube.services
|
||||
import android.app.Notification
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@ -28,6 +28,7 @@ import com.github.libretube.helpers.PlayerHelper
|
||||
import com.github.libretube.helpers.PlayerHelper.checkForSegments
|
||||
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.helpers.ProxyHelper
|
||||
import com.github.libretube.obj.PlayerNotificationData
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
@ -44,7 +45,7 @@ import kotlinx.serialization.encodeToString
|
||||
/**
|
||||
* Loads the selected videos audio in background mode with a notification area.
|
||||
*/
|
||||
class BackgroundMode : LifecycleService() {
|
||||
class OnlinePlayerService : LifecycleService() {
|
||||
/**
|
||||
* VideoId of the video
|
||||
*/
|
||||
@ -97,16 +98,14 @@ class BackgroundMode : LifecycleService() {
|
||||
*/
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// see https://developer.android.com/reference/android/app/Service#startForeground(int,%20android.app.Notification)
|
||||
val notification = Notification.Builder(this, BACKGROUND_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(getString(R.string.playingOnBackground))
|
||||
.setSmallIcon(R.drawable.ic_launcher_lockscreen)
|
||||
.build()
|
||||
|
||||
startForeground(PLAYER_NOTIFICATION_ID, notification)
|
||||
}
|
||||
val notification = NotificationCompat.Builder(this, BACKGROUND_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(getString(R.string.playingOnBackground))
|
||||
.setSmallIcon(R.drawable.ic_launcher_lockscreen)
|
||||
.build()
|
||||
|
||||
startForeground(PLAYER_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,10 +188,19 @@ class BackgroundMode : LifecycleService() {
|
||||
setMediaItem()
|
||||
|
||||
// create the notification
|
||||
if (!this@BackgroundMode::nowPlayingNotification.isInitialized) {
|
||||
nowPlayingNotification = NowPlayingNotification(this@BackgroundMode, player!!, true)
|
||||
if (!this@OnlinePlayerService::nowPlayingNotification.isInitialized) {
|
||||
nowPlayingNotification = NowPlayingNotification(
|
||||
this@OnlinePlayerService,
|
||||
player!!,
|
||||
true
|
||||
)
|
||||
}
|
||||
nowPlayingNotification.updatePlayerNotification(videoId, streams!!)
|
||||
val playerNotificationData = PlayerNotificationData(
|
||||
streams?.title,
|
||||
streams?.uploader,
|
||||
streams?.thumbnailUrl
|
||||
)
|
||||
nowPlayingNotification.updatePlayerNotification(videoId, playerNotificationData)
|
||||
|
||||
player?.apply {
|
||||
playWhenReady = playWhenReadyPlayer
|
||||
@ -259,7 +267,7 @@ class BackgroundMode : LifecycleService() {
|
||||
// show a toast on errors
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
Toast.makeText(
|
||||
this@BackgroundMode.applicationContext,
|
||||
this@OnlinePlayerService.applicationContext,
|
||||
error.localizedMessage,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
@ -351,7 +359,7 @@ class BackgroundMode : LifecycleService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy the [BackgroundMode] foreground service
|
||||
* destroy the [OnlinePlayerService] foreground service
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
// clear and reset the playing queue
|
||||
@ -371,7 +379,7 @@ class BackgroundMode : LifecycleService() {
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
// Return this instance of [BackgroundMode] so clients can call public methods
|
||||
fun getService(): BackgroundMode = this@BackgroundMode
|
||||
fun getService(): OnlinePlayerService = this@OnlinePlayerService
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
@ -77,6 +77,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
||||
.setTrackSelector(trackSelector)
|
||||
.setLoadControl(PlayerHelper.getLoadControl())
|
||||
.setAudioAttributes(PlayerHelper.getAudioAttributes(), true)
|
||||
.setUsePlatformDiagnostics(false)
|
||||
.build().apply {
|
||||
addListener(object : Player.Listener {
|
||||
override fun onEvents(player: Player, events: Player.Events) {
|
||||
|
@ -6,27 +6,24 @@ import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.DownloadedMediaRowBinding
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.obj.DownloadWithItems
|
||||
import com.github.libretube.extensions.formatAsFileSize
|
||||
import com.github.libretube.helpers.ImageHelper
|
||||
import com.github.libretube.ui.activities.OfflinePlayerActivity
|
||||
import com.github.libretube.ui.sheets.DownloadOptionsBottomSheet
|
||||
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
||||
import com.github.libretube.util.TextUtils
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.fileSize
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class DownloadsAdapter(
|
||||
private val context: Context,
|
||||
private val downloads: MutableList<DownloadWithItems>,
|
||||
private val toogleDownload: (DownloadWithItems) -> Boolean
|
||||
private val toggleDownload: (DownloadWithItems) -> Boolean
|
||||
) : RecyclerView.Adapter<DownloadsViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadsViewHolder {
|
||||
val binding = DownloadedMediaRowBinding.inflate(
|
||||
@ -75,7 +72,7 @@ class DownloadsAdapter(
|
||||
}
|
||||
|
||||
progressBar.setOnClickListener {
|
||||
val isDownloading = toogleDownload(downloads[position])
|
||||
val isDownloading = toggleDownload(downloads[position])
|
||||
|
||||
resumePauseBtn.setImageResource(
|
||||
if (isDownloading) {
|
||||
@ -93,24 +90,13 @@ class DownloadsAdapter(
|
||||
}
|
||||
|
||||
root.setOnLongClickListener {
|
||||
MaterialAlertDialogBuilder(root.context)
|
||||
.setTitle(R.string.delete)
|
||||
.setMessage(R.string.irreversible)
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
items.forEach {
|
||||
it.path.deleteIfExists()
|
||||
}
|
||||
download.thumbnailPath?.deleteIfExists()
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
||||
}
|
||||
downloads.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
DownloadOptionsBottomSheet(download, items) {
|
||||
downloads.removeAt(position)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, itemCount)
|
||||
}.show(
|
||||
(root.context as AppCompatActivity).supportFragmentManager
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import com.github.libretube.helpers.BackgroundHelper
|
||||
import com.github.libretube.helpers.ImageHelper
|
||||
import com.github.libretube.helpers.NavigationHelper
|
||||
import com.github.libretube.obj.ShareData
|
||||
import com.github.libretube.services.BackgroundMode
|
||||
import com.github.libretube.services.OnlinePlayerService
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import com.github.libretube.ui.dialogs.ShareDialog
|
||||
import com.github.libretube.ui.interfaces.AudioPlayerOptions
|
||||
@ -59,13 +59,13 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
private var handler = Handler(Looper.getMainLooper())
|
||||
private var isPaused: Boolean = false
|
||||
|
||||
private var playerService: BackgroundMode? = null
|
||||
private var playerService: OnlinePlayerService? = null
|
||||
|
||||
/** Defines callbacks for service binding, passed to bindService() */
|
||||
private val connection = object : ServiceConnection {
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
// We've bound to LocalService, cast the IBinder and get LocalService instance
|
||||
val binder = service as BackgroundMode.LocalBinder
|
||||
val binder = service as OnlinePlayerService.LocalBinder
|
||||
playerService = binder.getService()
|
||||
handleServiceConnection()
|
||||
}
|
||||
@ -77,7 +77,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
audioHelper = AudioHelper(requireContext())
|
||||
Intent(activity, BackgroundMode::class.java).also { intent ->
|
||||
Intent(activity, OnlinePlayerService::class.java).also { intent ->
|
||||
activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ import com.github.libretube.helpers.PlayerHelper.checkForSegments
|
||||
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.helpers.ProxyHelper
|
||||
import com.github.libretube.obj.PlayerNotificationData
|
||||
import com.github.libretube.obj.ShareData
|
||||
import com.github.libretube.obj.VideoResolution
|
||||
import com.github.libretube.services.DownloadService
|
||||
@ -1365,7 +1366,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
if (!this::nowPlayingNotification.isInitialized) {
|
||||
nowPlayingNotification = NowPlayingNotification(requireContext(), exoPlayer, false)
|
||||
}
|
||||
nowPlayingNotification.updatePlayerNotification(videoId!!, streams)
|
||||
val playerNotificationData = PlayerNotificationData(
|
||||
streams.title,
|
||||
streams.uploader,
|
||||
streams.thumbnailUrl
|
||||
)
|
||||
nowPlayingNotification.updatePlayerNotification(videoId!!, playerNotificationData)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.github.libretube.ui.sheets
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.obj.Download
|
||||
import com.github.libretube.db.obj.DownloadItem
|
||||
import com.github.libretube.services.OfflinePlayerService
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class DownloadOptionsBottomSheet(
|
||||
private val download: Download,
|
||||
private val items: List<DownloadItem>,
|
||||
private val onDelete: () -> Unit
|
||||
) : BaseBottomSheet() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val options = listOf(R.string.playOnBackground, R.string.delete).map { getString(it) }
|
||||
setSimpleItems(options) { selectedIndex ->
|
||||
when (selectedIndex) {
|
||||
0 -> {
|
||||
val playerIntent = Intent(requireContext(), OfflinePlayerService::class.java)
|
||||
.putExtra(IntentData.videoId, download.videoId)
|
||||
ContextCompat.startForegroundService(requireContext(), playerIntent)
|
||||
}
|
||||
1 -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.delete)
|
||||
.setMessage(R.string.irreversible)
|
||||
.setPositiveButton(R.string.okay) { _, _ ->
|
||||
items.forEach {
|
||||
it.path.deleteIfExists()
|
||||
}
|
||||
download.thumbnailPath?.deleteIfExists()
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
||||
}
|
||||
onDelete.invoke()
|
||||
dialog?.dismiss()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
@ -21,12 +21,12 @@ import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.os.bundleOf
|
||||
import coil.request.ImageRequest
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Streams
|
||||
import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
|
||||
import com.github.libretube.helpers.ImageHelper
|
||||
import com.github.libretube.helpers.PlayerHelper
|
||||
import com.github.libretube.obj.PlayerNotificationData
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.Player
|
||||
@ -41,11 +41,11 @@ class NowPlayingNotification(
|
||||
private val isBackgroundPlayerNotification: Boolean
|
||||
) {
|
||||
private var videoId: String? = null
|
||||
private var streams: Streams? = null
|
||||
private var notificationData: PlayerNotificationData? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
|
||||
/**
|
||||
* The [MediaSessionCompat] for the [streams].
|
||||
* The [MediaSessionCompat] for the [notificationData].
|
||||
*/
|
||||
private lateinit var mediaSession: MediaSessionCompat
|
||||
|
||||
@ -68,7 +68,7 @@ class NowPlayingNotification(
|
||||
* sets the title of the notification
|
||||
*/
|
||||
override fun getCurrentContentTitle(player: Player): CharSequence {
|
||||
return streams?.title!!
|
||||
return notificationData?.title.orEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +92,7 @@ class NowPlayingNotification(
|
||||
* the description of the notification (below the title)
|
||||
*/
|
||||
override fun getCurrentContentText(player: Player): CharSequence? {
|
||||
return streams?.uploader
|
||||
return notificationData?.uploaderName
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,21 +110,25 @@ class NowPlayingNotification(
|
||||
}
|
||||
|
||||
override fun getCurrentSubText(player: Player): CharSequence? {
|
||||
return streams?.uploader
|
||||
return notificationData?.uploaderName
|
||||
}
|
||||
}
|
||||
|
||||
private fun enqueueThumbnailRequest(callback: PlayerNotificationManager.BitmapCallback) {
|
||||
// If playing a downloaded file, show the downloaded thumbnail instead of loading an
|
||||
// online image
|
||||
notificationData?.thumbnailPath?.let { path ->
|
||||
ImageHelper.getDownloadedImage(context, path)?.let {
|
||||
bitmap = processThumbnailBitmap(it)
|
||||
callback.onBitmap(bitmap!!)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(streams?.thumbnailUrl)
|
||||
.data(notificationData?.thumbnailUrl)
|
||||
.target {
|
||||
val bm = it.toBitmap()
|
||||
// returns the bitmap on Android 13+, for everything below scaled down to a square
|
||||
bitmap = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
ImageHelper.getSquareBitmap(bm)
|
||||
} else {
|
||||
bm
|
||||
}
|
||||
bitmap = processThumbnailBitmap(it.toBitmap())
|
||||
callback.onBitmap(bitmap!!)
|
||||
}
|
||||
.build()
|
||||
@ -155,6 +159,17 @@ class NowPlayingNotification(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitmap on Android 13+, for everything below scaled down to a square
|
||||
*/
|
||||
private fun processThumbnailBitmap(bitmap: Bitmap): Bitmap {
|
||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
ImageHelper.getSquareBitmap(bitmap)
|
||||
} else {
|
||||
bitmap
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNotificationAction(drawableRes: Int, actionName: String, instanceId: Int): NotificationCompat.Action {
|
||||
val intent = Intent(actionName).setPackage(context.packageName)
|
||||
val pendingIntent = PendingIntentCompat
|
||||
@ -194,16 +209,14 @@ class NowPlayingNotification(
|
||||
context.resources,
|
||||
R.drawable.ic_launcher_monochrome
|
||||
)
|
||||
val title = streams?.title!!
|
||||
val uploader = streams?.uploader
|
||||
val extras = bundleOf(
|
||||
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON to appIcon,
|
||||
MediaMetadataCompat.METADATA_KEY_TITLE to title,
|
||||
MediaMetadataCompat.METADATA_KEY_ARTIST to uploader
|
||||
MediaMetadataCompat.METADATA_KEY_TITLE to notificationData?.title,
|
||||
MediaMetadataCompat.METADATA_KEY_ARTIST to notificationData?.uploaderName
|
||||
)
|
||||
return MediaDescriptionCompat.Builder()
|
||||
.setTitle(title)
|
||||
.setSubtitle(uploader)
|
||||
.setTitle(notificationData?.title)
|
||||
.setSubtitle(notificationData?.uploaderName)
|
||||
.setIconBitmap(appIcon)
|
||||
.setExtras(extras)
|
||||
.build()
|
||||
@ -252,10 +265,10 @@ class NowPlayingNotification(
|
||||
*/
|
||||
fun updatePlayerNotification(
|
||||
videoId: String,
|
||||
streams: Streams
|
||||
data: PlayerNotificationData
|
||||
) {
|
||||
this.videoId = videoId
|
||||
this.streams = streams
|
||||
this.notificationData = data
|
||||
// reset the thumbnail bitmap in order to become reloaded for the new video
|
||||
this.bitmap = null
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user