|
|
@ -1,4 +1,4 @@ |
|
|
|
package com.github.apognu.otter.playback |
|
|
|
package org.funkwhale.ffa.playback |
|
|
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint |
|
|
|
import android.annotation.SuppressLint |
|
|
|
import android.app.Service |
|
|
|
import android.app.Service |
|
|
@ -15,9 +15,6 @@ import android.support.v4.media.MediaMetadataCompat |
|
|
|
import android.view.KeyEvent |
|
|
|
import android.view.KeyEvent |
|
|
|
import androidx.core.app.NotificationManagerCompat |
|
|
|
import androidx.core.app.NotificationManagerCompat |
|
|
|
import androidx.media.session.MediaButtonReceiver |
|
|
|
import androidx.media.session.MediaButtonReceiver |
|
|
|
import com.github.apognu.otter.Otter |
|
|
|
|
|
|
|
import com.github.apognu.otter.R |
|
|
|
|
|
|
|
import com.github.apognu.otter.utils.* |
|
|
|
|
|
|
|
import com.google.android.exoplayer2.C |
|
|
|
import com.google.android.exoplayer2.C |
|
|
|
import com.google.android.exoplayer2.ExoPlaybackException |
|
|
|
import com.google.android.exoplayer2.ExoPlaybackException |
|
|
|
import com.google.android.exoplayer2.Player |
|
|
|
import com.google.android.exoplayer2.Player |
|
|
@ -29,6 +26,9 @@ import kotlinx.coroutines.* |
|
|
|
import kotlinx.coroutines.Dispatchers.IO |
|
|
|
import kotlinx.coroutines.Dispatchers.IO |
|
|
|
import kotlinx.coroutines.Dispatchers.Main |
|
|
|
import kotlinx.coroutines.Dispatchers.Main |
|
|
|
import kotlinx.coroutines.flow.collect |
|
|
|
import kotlinx.coroutines.flow.collect |
|
|
|
|
|
|
|
import org.funkwhale.ffa.FFA |
|
|
|
|
|
|
|
import org.funkwhale.ffa.R |
|
|
|
|
|
|
|
import org.funkwhale.ffa.utils.* |
|
|
|
|
|
|
|
|
|
|
|
class PlayerService : Service() { |
|
|
|
class PlayerService : Service() { |
|
|
|
companion object { |
|
|
|
companion object { |
|
|
@ -62,10 +62,13 @@ class PlayerService : Service() { |
|
|
|
intent.extras?.getParcelable<KeyEvent>(Intent.EXTRA_KEY_EVENT)?.let { key -> |
|
|
|
intent.extras?.getParcelable<KeyEvent>(Intent.EXTRA_KEY_EVENT)?.let { key -> |
|
|
|
when (key.keyCode) { |
|
|
|
when (key.keyCode) { |
|
|
|
KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> { |
|
|
|
KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> { |
|
|
|
if (hasAudioFocus(true)) MediaButtonReceiver.handleIntent(Otter.get().mediaSession.session, intent) |
|
|
|
if (hasAudioFocus(true)) MediaButtonReceiver.handleIntent( |
|
|
|
|
|
|
|
FFA.get().mediaSession.session, |
|
|
|
|
|
|
|
intent |
|
|
|
|
|
|
|
) |
|
|
|
Unit |
|
|
|
Unit |
|
|
|
} |
|
|
|
} |
|
|
|
else -> MediaButtonReceiver.handleIntent(Otter.get().mediaSession.session, intent) |
|
|
|
else -> MediaButtonReceiver.handleIntent(FFA.get().mediaSession.session, intent) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -105,7 +108,7 @@ class PlayerService : Service() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mediaControlsManager = MediaControlsManager(this, scope, Otter.get().mediaSession.session) |
|
|
|
mediaControlsManager = MediaControlsManager(this, scope, FFA.get().mediaSession.session) |
|
|
|
|
|
|
|
|
|
|
|
player = SimpleExoPlayer.Builder(this).build().apply { |
|
|
|
player = SimpleExoPlayer.Builder(this).build().apply { |
|
|
|
playWhenReady = false |
|
|
|
playWhenReady = false |
|
|
@ -115,9 +118,9 @@ class PlayerService : Service() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Otter.get().mediaSession.active = true |
|
|
|
FFA.get().mediaSession.active = true |
|
|
|
|
|
|
|
|
|
|
|
Otter.get().mediaSession.connector.apply { |
|
|
|
FFA.get().mediaSession.connector.apply { |
|
|
|
setPlayer(player) |
|
|
|
setPlayer(player) |
|
|
|
|
|
|
|
|
|
|
|
setMediaMetadataProvider { |
|
|
|
setMediaMetadataProvider { |
|
|
@ -137,7 +140,10 @@ class PlayerService : Service() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
registerReceiver(headphonesUnpluggedReceiver, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) |
|
|
|
registerReceiver( |
|
|
|
|
|
|
|
headphonesUnpluggedReceiver, |
|
|
|
|
|
|
|
IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY) |
|
|
|
|
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun watchEventBus() { |
|
|
|
private fun watchEventBus() { |
|
|
@ -197,7 +203,12 @@ class PlayerService : Service() { |
|
|
|
is Command.SetRepeatMode -> player.repeatMode = command.mode |
|
|
|
is Command.SetRepeatMode -> player.repeatMode = command.mode |
|
|
|
|
|
|
|
|
|
|
|
is Command.PinTrack -> PinService.download(this@PlayerService, command.track) |
|
|
|
is Command.PinTrack -> PinService.download(this@PlayerService, command.track) |
|
|
|
is Command.PinTracks -> command.tracks.forEach { PinService.download(this@PlayerService, it) } |
|
|
|
is Command.PinTracks -> command.tracks.forEach { |
|
|
|
|
|
|
|
PinService.download( |
|
|
|
|
|
|
|
this@PlayerService, |
|
|
|
|
|
|
|
it |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -260,7 +271,7 @@ class PlayerService : Service() { |
|
|
|
setPlaybackState(false) |
|
|
|
setPlaybackState(false) |
|
|
|
player.release() |
|
|
|
player.release() |
|
|
|
|
|
|
|
|
|
|
|
Otter.get().mediaSession.active = false |
|
|
|
FFA.get().mediaSession.active = false |
|
|
|
|
|
|
|
|
|
|
|
super.onDestroy() |
|
|
|
super.onDestroy() |
|
|
|
} |
|
|
|
} |
|
|
@ -330,11 +341,17 @@ class PlayerService : Service() { |
|
|
|
return mediaMetadataBuilder.apply { |
|
|
|
return mediaMetadataBuilder.apply { |
|
|
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, track.title) |
|
|
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, track.title) |
|
|
|
putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track.artist.name) |
|
|
|
putString(MediaMetadataCompat.METADATA_KEY_ARTIST, track.artist.name) |
|
|
|
putLong(MediaMetadata.METADATA_KEY_DURATION, (track.bestUpload()?.duration?.toLong() ?: 0L) * 1000) |
|
|
|
putLong( |
|
|
|
|
|
|
|
MediaMetadata.METADATA_KEY_DURATION, |
|
|
|
|
|
|
|
(track.bestUpload()?.duration?.toLong() ?: 0L) * 1000 |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
runBlocking(IO) { |
|
|
|
runBlocking(IO) { |
|
|
|
this@apply.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, Picasso.get().load(coverUrl).get()) |
|
|
|
this@apply.putBitmap( |
|
|
|
|
|
|
|
MediaMetadataCompat.METADATA_KEY_ALBUM_ART, |
|
|
|
|
|
|
|
Picasso.get().load(coverUrl).get() |
|
|
|
|
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
} catch (e: Exception) { |
|
|
|
} |
|
|
|
} |
|
|
@ -361,7 +378,11 @@ class PlayerService : Service() { |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
@Suppress("DEPRECATION") |
|
|
|
@Suppress("DEPRECATION") |
|
|
|
audioManager.requestAudioFocus(audioFocusChangeListener, AudioAttributes.CONTENT_TYPE_MUSIC, AudioManager.AUDIOFOCUS_GAIN).let { |
|
|
|
audioManager.requestAudioFocus( |
|
|
|
|
|
|
|
audioFocusChangeListener, |
|
|
|
|
|
|
|
AudioAttributes.CONTENT_TYPE_MUSIC, |
|
|
|
|
|
|
|
AudioManager.AUDIOFOCUS_GAIN |
|
|
|
|
|
|
|
).let { |
|
|
|
allowed = when (it) { |
|
|
|
allowed = when (it) { |
|
|
|
AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> true |
|
|
|
AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> true |
|
|
|
else -> false |
|
|
|
else -> false |
|
|
@ -425,7 +446,10 @@ class PlayerService : Service() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
override fun onTracksChanged(trackGroups: TrackGroupArray, trackSelections: TrackSelectionArray) { |
|
|
|
override fun onTracksChanged( |
|
|
|
|
|
|
|
trackGroups: TrackGroupArray, |
|
|
|
|
|
|
|
trackSelections: TrackSelectionArray |
|
|
|
|
|
|
|
) { |
|
|
|
super.onTracksChanged(trackGroups, trackSelections) |
|
|
|
super.onTracksChanged(trackGroups, trackSelections) |
|
|
|
|
|
|
|
|
|
|
|
if (queue.current != player.currentWindowIndex) { |
|
|
|
if (queue.current != player.currentWindowIndex) { |
|
|
@ -433,7 +457,9 @@ class PlayerService : Service() { |
|
|
|
mediaControlsManager.updateNotification(queue.current(), player.playWhenReady) |
|
|
|
mediaControlsManager.updateNotification(queue.current(), player.playWhenReady) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (queue.get().isNotEmpty() && queue.current() == queue.get().last() && radioPlayer.isActive()) { |
|
|
|
if (queue.get().isNotEmpty() && queue.current() == queue.get() |
|
|
|
|
|
|
|
.last() && radioPlayer.isActive() |
|
|
|
|
|
|
|
) { |
|
|
|
scope.launch(IO) { |
|
|
|
scope.launch(IO) { |
|
|
|
if (radioPlayer.lock.tryAcquire()) { |
|
|
|
if (radioPlayer.lock.tryAcquire()) { |
|
|
|
radioPlayer.prepareNextTrack() |
|
|
|
radioPlayer.prepareNextTrack() |
|
|
@ -495,4 +521,4 @@ class PlayerService : Service() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |