Add Audio Focus

Use the built-in ExoPlayer AudioFocus functionality to implement Audio Focus [hopefully].
This commit is contained in:
OxygenCobalt 2020-10-30 16:06:45 -06:00
parent 535fc95f71
commit a4f55873ec
2 changed files with 30 additions and 5 deletions

View file

@ -17,14 +17,15 @@ import android.support.v4.media.session.MediaSessionCompat
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.audio.AudioAttributes
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
@ -59,6 +60,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
private val playbackManager = PlaybackStateManager.getInstance() private val playbackManager = PlaybackStateManager.getInstance()
private lateinit var mediaSession: MediaSessionCompat private lateinit var mediaSession: MediaSessionCompat
private lateinit var systemReceiver: SystemEventReceiver private lateinit var systemReceiver: SystemEventReceiver
private var changeIsFromSystem = false
private val serviceJob = Job() private val serviceJob = Job()
private val serviceScope = CoroutineScope( private val serviceScope = CoroutineScope(
@ -91,6 +93,15 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
handleMediaButtonEvent(mediaButtonEvent) handleMediaButtonEvent(mediaButtonEvent)
} }
// Set up AudioFocus/AudioAttributes
player.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build(),
true
)
notification = createNotification() notification = createNotification()
// Set up callback for system events // Set up callback for system events
@ -132,6 +143,16 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
} }
} }
override fun onIsPlayingChanged(isPlaying: Boolean) {
// If the change to playing occurred from the system instead of PlaybackStateManager, then
// sync the playing value to PlaybackStateManager to keep it up ton date.
if (isPlaying != playbackManager.isPlaying && changeIsFromSystem) {
playbackManager.setPlayingStatus(isPlaying)
}
changeIsFromSystem = true
}
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
// If the song loops while in the LOOP_ONCE mode, then stop looping after that. // If the song loops while in the LOOP_ONCE mode, then stop looping after that.
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT && if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT &&
@ -144,6 +165,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
// --- PLAYBACK STATE CALLBACK OVERRIDES --- // --- PLAYBACK STATE CALLBACK OVERRIDES ---
override fun onSongUpdate(song: Song?) { override fun onSongUpdate(song: Song?) {
changeIsFromSystem = false
song?.let { song?.let {
val item = MediaItem.fromUri(it.id.toURI()) val item = MediaItem.fromUri(it.id.toURI())
player.setMediaItem(item) player.setMediaItem(item)
@ -157,6 +180,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
} }
override fun onPlayingUpdate(isPlaying: Boolean) { override fun onPlayingUpdate(isPlaying: Boolean) {
changeIsFromSystem = false
if (isPlaying && !player.isPlaying) { if (isPlaying && !player.isPlaying) {
player.play() player.play()
@ -171,6 +196,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
} }
override fun onLoopUpdate(mode: LoopMode) { override fun onLoopUpdate(mode: LoopMode) {
changeIsFromSystem = false
when (mode) { when (mode) {
LoopMode.NONE -> { LoopMode.NONE -> {
player.repeatMode = Player.REPEAT_MODE_OFF player.repeatMode = Player.REPEAT_MODE_OFF
@ -182,6 +209,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
} }
override fun onSeekConfirm(position: Long) { override fun onSeekConfirm(position: Long) {
changeIsFromSystem = false
player.seekTo(position * 1000) player.seekTo(position * 1000)
} }

View file

@ -389,9 +389,5 @@ class PlaybackStateManager {
return newInstance return newInstance
} }
} }
const val LOOP_NONE = 0
const val LOOP_ONCE = 1
const val LOOP_ENDLESS = 2
} }
} }