diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f93b57f..2ff9843 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -167,18 +167,18 @@ dependencies { implementation("com.google.android.material:material:1.6.1") implementation("com.android.support.constraint:constraint-layout:2.0.4") - implementation("com.google.android.exoplayer:exoplayer-core:2.14.2") - implementation("com.google.android.exoplayer:exoplayer-ui:2.14.2") - implementation("com.google.android.exoplayer:extension-mediasession:2.14.2") + implementation("com.google.android.exoplayer:exoplayer-core:2.18.1") + implementation("com.google.android.exoplayer:exoplayer-ui:2.18.1") + implementation("com.google.android.exoplayer:extension-mediasession:2.18.1") implementation("io.insert-koin:koin-core:3.1.2") implementation("io.insert-koin:koin-android:3.1.2") testImplementation("io.insert-koin:koin-test:3.1.2") - implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-opus:2.14.0") { + implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-opus:789a4f83169cff5c7a91655bb828fde2cfde671a") { isTransitive = false } - implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-flac:2.14.0") { + implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-flac:789a4f83169cff5c7a91655bb828fde2cfde671a") { isTransitive = false } diff --git a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt index b3e2d68..efe6eb6 100644 --- a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt +++ b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt @@ -473,11 +473,9 @@ class MainActivity : AppCompatActivity() { binding.nowPlayingContainer?.nowPlayingTitle?.text = track.title binding.nowPlayingContainer?.nowPlayingAlbum?.text = track.artist.name - binding.nowPlayingContainer?.nowPlayingToggle?.icon = getDrawable(R.drawable.pause) binding.nowPlayingContainer?.nowPlayingDetailsTitle?.text = track.title binding.nowPlayingContainer?.nowPlayingDetailsArtist?.text = track.artist.name - binding.nowPlayingContainer?.nowPlayingDetailsToggle?.icon = getDrawable(R.drawable.pause) Picasso.get() .maybeLoad(maybeNormalizeUrl(track.album?.cover?.urls?.original)) diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt b/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt index 4747a2e..ce3d88c 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt @@ -41,7 +41,7 @@ class MediaSession(private val context: Context) { MediaSessionConnector(session).also { it.setQueueNavigator(FFAQueueNavigator()) - it.setMediaButtonEventHandler { _, _, intent -> + it.setMediaButtonEventHandler { _, intent -> if (!active) { Intent(context, PlayerService::class.java).let { player -> player.action = intent.action @@ -65,13 +65,11 @@ class MediaSession(private val context: Context) { } class FFAQueueNavigator : MediaSessionConnector.QueueNavigator { - override fun onSkipToQueueItem(player: Player, controlDispatcher: ControlDispatcher, id: Long) { + override fun onSkipToQueueItem(player: Player, id: Long) { CommandBus.send(Command.PlayTrack(id.toInt())) } - override fun onCurrentWindowIndexChanged(player: Player) {} - - override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?) = true + override fun onCommand(player: Player, command: String, extras: Bundle?, cb: ResultReceiver?) = true override fun getSupportedQueueNavigatorActions(player: Player): Long { return PlaybackStateCompat.ACTION_PLAY_PAUSE or @@ -80,13 +78,13 @@ class FFAQueueNavigator : MediaSessionConnector.QueueNavigator { PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM } - override fun onSkipToNext(player: Player, controlDispatcher: ControlDispatcher) { + override fun onSkipToNext(player: Player) { CommandBus.send(Command.NextTrack) } - override fun getActiveQueueItemId(player: Player?) = player?.currentWindowIndex?.toLong() ?: 0 + override fun getActiveQueueItemId(player: Player?) = player?.currentMediaItemIndex?.toLong() ?: 0 - override fun onSkipToPrevious(player: Player, controlDispatcher: ControlDispatcher) { + override fun onSkipToPrevious(player: Player) { CommandBus.send(Command.PreviousTrack) } diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt b/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt index f3bd1ce..321c125 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt @@ -80,14 +80,16 @@ class PinService : DownloadService(AppContext.NOTIFICATION_DOWNLOADS) { override fun getScheduler(): Scheduler? = null - override fun getForegroundNotification(downloads: MutableList): Notification { + override fun getForegroundNotification(downloads: MutableList, + notMetRequirements: Int): Notification { val description = resources.getQuantityString(R.plurals.downloads_description, downloads.size, downloads.size) return DownloadNotificationHelper( this, AppContext.NOTIFICATION_CHANNEL_DOWNLOADS - ).buildProgressNotification(this, R.drawable.downloads, null, description, downloads) + ).buildProgressNotification(this, R.drawable.downloads, null, description, + downloads, notMetRequirements) } private fun getDownloads() = downloadManager.downloadIndex.getDownloads() diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt b/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt index 9c74da5..68adff5 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt @@ -31,11 +31,10 @@ import audio.funkwhale.ffa.utils.log import audio.funkwhale.ffa.utils.maybeNormalizeUrl import audio.funkwhale.ffa.utils.onApi import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.ExoPlaybackException +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.SimpleExoPlayer -import com.google.android.exoplayer2.source.TrackGroupArray -import com.google.android.exoplayer2.trackselection.TrackSelectionArray +import com.google.android.exoplayer2.Tracks import com.squareup.picasso.Picasso import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -65,7 +64,7 @@ class PlayerService : Service() { private lateinit var queue: QueueManager private lateinit var mediaControlsManager: MediaControlsManager - private lateinit var player: SimpleExoPlayer + private lateinit var player: ExoPlayer private val mediaMetadataBuilder = MediaMetadataCompat.Builder() @@ -132,12 +131,13 @@ class PlayerService : Service() { mediaControlsManager = MediaControlsManager(this, scope, mediaSession.session) - player = SimpleExoPlayer.Builder(this).build().apply { + player = ExoPlayer.Builder(this).build().apply { playWhenReady = false playerEventListener = PlayerEventListener().also { addListener(it) } + EventBus.send(Event.StateChanged(this.isPlaying())) } mediaSession.active = true @@ -151,7 +151,8 @@ class PlayerService : Service() { } if (queue.current > -1) { - player.prepare(queue.dataSources) + player.setMediaSource(queue.dataSources) + player.prepare() FFACache.getLine(this, "progress")?.let { player.seekTo(queue.current, it.toLong()) @@ -180,7 +181,8 @@ class PlayerService : Service() { if (!command.fromRadio) radioPlayer.stop() queue.replace(command.queue) - player.prepare(queue.dataSources, true, true) + player.setMediaSource(queue.dataSources) + player.prepare() setPlaybackState(true) @@ -307,7 +309,8 @@ class PlayerService : Service() { } if (state && player.playbackState == Player.STATE_IDLE) { - player.prepare(queue.dataSources) + player.setMediaSource(queue.dataSources) + player.prepare() } if (hasAudioFocus(state)) { @@ -318,7 +321,7 @@ class PlayerService : Service() { } private fun togglePlayback() { - setPlaybackState(!player.playWhenReady) + setPlaybackState(!player.isPlaying) } private fun skipToPreviousTrack() { @@ -326,11 +329,11 @@ class PlayerService : Service() { return player.seekTo(0) } - player.previous() + player.seekToPrevious() } private fun skipToNextTrack() { - player.next() + player.seekToNext() FFACache.set(this@PlayerService, "progress", "0") ProgressBus.send(0, 0, 0) @@ -419,9 +422,14 @@ class PlayerService : Service() { } @SuppressLint("NewApi") - inner class PlayerEventListener : Player.EventListener { - override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { - super.onPlayerStateChanged(playWhenReady, playbackState) + inner class PlayerEventListener : Player.Listener { + override fun onIsPlayingChanged(isPlaying: Boolean) { + super.onIsPlayingChanged(isPlaying) + mediaControlsManager.updateNotification(queue.current(), isPlaying) + } + + override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { + super.onPlayWhenReadyChanged(playWhenReady, reason) EventBus.send(Event.StateChanged(playWhenReady)) @@ -429,55 +437,45 @@ class PlayerService : Service() { CommandBus.send(Command.RefreshTrack(queue.current())) } - when (playWhenReady) { - true -> { - when (playbackState) { - Player.STATE_READY -> mediaControlsManager.updateNotification(queue.current(), true) - Player.STATE_BUFFERING -> EventBus.send(Event.Buffering(true)) - Player.STATE_ENDED -> { - setPlaybackState(false) + if (!playWhenReady) { + Build.VERSION_CODES.N.onApi( + { stopForeground(STOP_FOREGROUND_DETACH) }, + { stopForeground(false) } + ) + } + } - queue.current = 0 - player.seekTo(0, C.TIME_UNSET) + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + EventBus.send(Event.Buffering(playbackState == Player.STATE_BUFFERING)) + when (playbackState) { + Player.STATE_ENDED -> { + setPlaybackState(false) - ProgressBus.send(0, 0, 0) - } + queue.current = 0 + player.seekTo(0, C.TIME_UNSET) - Player.STATE_IDLE -> { - setPlaybackState(false) - - return EventBus.send(Event.PlaybackStopped) - } - } - - if (playbackState != Player.STATE_BUFFERING) EventBus.send(Event.Buffering(false)) + ProgressBus.send(0, 0, 0) } - false -> { - EventBus.send(Event.Buffering(false)) + Player.STATE_IDLE -> { + setPlaybackState(false) - Build.VERSION_CODES.N.onApi( - { stopForeground(STOP_FOREGROUND_DETACH) }, - { stopForeground(false) } - ) + EventBus.send(Event.PlaybackStopped) - when (playbackState) { - Player.STATE_READY -> mediaControlsManager.updateNotification(queue.current(), false) - Player.STATE_IDLE -> mediaControlsManager.remove() + if (!player.playWhenReady) { + mediaControlsManager.remove() } } } } - override fun onTracksChanged( - trackGroups: TrackGroupArray, - trackSelections: TrackSelectionArray - ) { - super.onTracksChanged(trackGroups, trackSelections) + override fun onTracksChanged(tracks: Tracks) { + super.onTracksChanged(tracks) - if (queue.current != player.currentWindowIndex) { - queue.current = player.currentWindowIndex - mediaControlsManager.updateNotification(queue.current(), player.playWhenReady) + if (queue.current != player.currentMediaItemIndex) { + queue.current = player.currentMediaItemIndex + mediaControlsManager.updateNotification(queue.current(), player.isPlaying) } if (queue.get().isNotEmpty() && @@ -510,13 +508,14 @@ class PlayerService : Service() { } } - override fun onPlayerError(error: ExoPlaybackException) { + override fun onPlayerError(error: PlaybackException) { EventBus.send(Event.PlaybackError(getString(R.string.error_playback))) if (player.playWhenReady) { queue.current++ - player.prepare(queue.dataSources, true, true) + player.setMediaSource(queue.dataSources, true) player.seekTo(queue.current, 0) + player.prepare() CommandBus.send(Command.RefreshTrack(queue.current())) } diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt b/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt index fe9bf31..821b860 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt @@ -12,6 +12,7 @@ import audio.funkwhale.ffa.utils.FFACache import audio.funkwhale.ffa.utils.log import audio.funkwhale.ffa.utils.mustNormalizeUrl import com.github.kittinunf.fuel.gson.gsonDeserializerOf +import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.source.ConcatenatingMediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.gson.Gson @@ -38,8 +39,8 @@ class QueueManager(val context: Context) { metadata.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - ProgressiveMediaSource.Factory(factory).setTag(track.title) - .createMediaSource(Uri.parse(url)) + val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build() + ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem) } ) } @@ -63,8 +64,8 @@ class QueueManager(val context: Context) { val factory = cacheDataSourceFactoryProvider.create(context) val sources = tracks.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - - ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url)) + val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build() + ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem) } metadata = tracks.toMutableList() @@ -84,7 +85,8 @@ class QueueManager(val context: Context) { val sources = missingTracks.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)) + val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build() + ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem) } metadata.addAll(tracks) @@ -101,7 +103,8 @@ class QueueManager(val context: Context) { val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") if (metadata.indexOf(track) == -1) { - ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)).let { + val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build() + ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem).let { dataSources.addMediaSource(current + 1, it) metadata.add(current + 1, track) }