Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add audio usage type so it's possible to force audio from earpiece speaker #105

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.doublesymmetry.kotlinaudio.models

data class AudioAttributeConfig(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit on the fence about introducing this intermediate class just to encapsulate the arguments passed to the setAudio function. @dcvz will need to weigh in on it.


/**
* Whether audio focus should be managed automatically. See https://medium.com/google-exoplayer/easy-audio-focus-with-exoplayer-a2dcbbe4640e
*/
val handleAudioFocus: Boolean = false,
/**
* The audio content type.
*/
val audioContentType: AudioContentType = AudioContentType.MUSIC,

/**
* The audio usage type.
*/
var audioUsageType: AudioUsageType = AudioUsageType.MEDIA
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a newline at the end of the file.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.doublesymmetry.kotlinaudio.models

enum class AudioUsageType {
MEDIA,
VOICE_COMMUNICATION,
}

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ data class PlayerConfig(
* Whether audio focus should be managed automatically. See https://medium.com/google-exoplayer/easy-audio-focus-with-exoplayer-a2dcbbe4640e
*/
val handleAudioFocus: Boolean = false,
/**
* The audio usage type.
*/
val audioUsageType: AudioUsageType = AudioUsageType.MEDIA,
/**
* The audio content type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import androidx.media.AudioManagerCompat.AUDIOFOCUS_GAIN
import com.doublesymmetry.kotlinaudio.event.EventHolder
import com.doublesymmetry.kotlinaudio.event.NotificationEventHolder
import com.doublesymmetry.kotlinaudio.event.PlayerEventHolder
import com.doublesymmetry.kotlinaudio.models.AudioAttributeConfig
import com.doublesymmetry.kotlinaudio.models.AudioContentType
import com.doublesymmetry.kotlinaudio.models.AudioItem
import com.doublesymmetry.kotlinaudio.models.AudioItemHolder
import com.doublesymmetry.kotlinaudio.models.AudioItemTransitionReason
import com.doublesymmetry.kotlinaudio.models.AudioPlayerState
import com.doublesymmetry.kotlinaudio.models.AudioUsageType
import com.doublesymmetry.kotlinaudio.models.BufferConfig
import com.doublesymmetry.kotlinaudio.models.CacheConfig
import com.doublesymmetry.kotlinaudio.models.DefaultPlayerOptions
Expand Down Expand Up @@ -250,21 +252,8 @@ abstract class BaseAudioPlayer internal constructor(
exoPlayer.addListener(PlayerListener())

scope.launch {
// Whether ExoPlayer should manage audio focus for us automatically
// see https://medium.com/google-exoplayer/easy-audio-focus-with-exoplayer-a2dcbbe4640e
val audioAttributes = AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(
when (playerConfig.audioContentType) {
AudioContentType.MUSIC -> C.AUDIO_CONTENT_TYPE_MUSIC
AudioContentType.SPEECH -> C.AUDIO_CONTENT_TYPE_SPEECH
AudioContentType.SONIFICATION -> C.AUDIO_CONTENT_TYPE_SONIFICATION
AudioContentType.MOVIE -> C.AUDIO_CONTENT_TYPE_MOVIE
AudioContentType.UNKNOWN -> C.AUDIO_CONTENT_TYPE_UNKNOWN
}
)
.build();
exoPlayer.setAudioAttributes(audioAttributes, playerConfig.handleAudioFocus);
setAudioAttributes()

mediaSessionConnector.setPlayer(playerToUse)
mediaSessionConnector.setMediaMetadataProvider {
notificationManager.getMediaMetadataCompat()
Expand All @@ -274,6 +263,53 @@ abstract class BaseAudioPlayer internal constructor(
playerEventHolder.updateAudioPlayerState(AudioPlayerState.IDLE)
}

fun setAudioAttributes(audioAttributeConfig: AudioAttributeConfig) {
scope.launch {
playerConfig = PlayerConfig(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps handleAudioFocus, audioUsageType, audioContentType should be updated to var's so that they could simply be mutated rather than replacing the entire PlayerConfig object. Not sure.

interceptPlayerActionsTriggeredExternally = playerConfig.interceptPlayerActionsTriggeredExternally,
handleAudioBecomingNoisy = playerConfig.handleAudioBecomingNoisy,
wakeMode = playerConfig.wakeMode,

handleAudioFocus = audioAttributeConfig.handleAudioFocus,
audioUsageType = audioAttributeConfig.audioUsageType,
audioContentType = audioAttributeConfig.audioContentType,
)

setAudioAttributes()
}
}

private fun setAudioAttributes() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't personally love the function overloading here. Perhaps we could name this function something like applyAudioAttribute().

val audioAttributes = AudioAttributes.Builder()
.setUsage(
when (playerConfig.audioUsageType) {
AudioUsageType.MEDIA -> C.USAGE_MEDIA
AudioUsageType.VOICE_COMMUNICATION -> C.USAGE_VOICE_COMMUNICATION
}
)
.setContentType(
when (playerConfig.audioContentType) {
AudioContentType.MUSIC -> C.AUDIO_CONTENT_TYPE_MUSIC
AudioContentType.SPEECH -> C.AUDIO_CONTENT_TYPE_SPEECH
AudioContentType.SONIFICATION -> C.AUDIO_CONTENT_TYPE_SONIFICATION
AudioContentType.MOVIE -> C.AUDIO_CONTENT_TYPE_MOVIE
AudioContentType.UNKNOWN -> C.AUDIO_CONTENT_TYPE_UNKNOWN
}
)
.build();

try {
var handleAudioFocus = playerConfig.handleAudioFocus
if (audioAttributes.usage != C.USAGE_MEDIA || audioAttributes.usage != C.USAGE_GAME) {
handleAudioFocus = false
}

exoPlayer.setAudioAttributes(audioAttributes, handleAudioFocus);
} catch (e: Exception) {
Timber.e("Error setting audioAttributes: ", e)
}
}

private fun createForwardingPlayer(): ForwardingPlayer {
return object : ForwardingPlayer(exoPlayer) {
override fun play() {
Expand Down