playback: use factory pattern

This commit is contained in:
Alexander Capehart 2024-09-13 13:35:46 -06:00
parent 8418dccdc6
commit a3af24688a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 81 additions and 50 deletions

View file

@ -38,16 +38,18 @@ import org.oxycblt.auxio.playback.service.PlaybackServiceFragment
@AndroidEntryPoint
class AuxioService :
MediaBrowserServiceCompat(), ForegroundListener, MusicServiceFragment.Invalidator {
@Inject lateinit var playbackFragment: PlaybackServiceFragment
MediaBrowserServiceCompat(), ForegroundListener, MusicServiceFragment.Invalidator {
@Inject lateinit var playbackFragmentFactory: PlaybackServiceFragment.Factory
private lateinit var playbackFragment: PlaybackServiceFragment
@Inject lateinit var musicFragmentFactory: MusicServiceFragment.Factory
lateinit var musicFragment: MusicServiceFragment
private lateinit var musicFragment: MusicServiceFragment
@SuppressLint("WrongConstant")
override fun onCreate() {
super.onCreate()
sessionToken = playbackFragment.attach(this)
playbackFragment = playbackFragmentFactory.create(this, this)
sessionToken = playbackFragment.token
musicFragment = musicFragmentFactory.create(this, this, this)
}

View file

@ -44,12 +44,17 @@ import org.oxycblt.auxio.util.logD
*
* @author Alexander Capehart (OxygenCobalt)
*/
class ReplayGainAudioProcessor
@Inject
constructor(
class ReplayGainAudioProcessor private constructor(
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings
) : BaseAudioProcessor(), PlaybackStateManager.Listener, PlaybackSettings.Listener {
class Factory @Inject constructor(
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings
) {
fun create() = ReplayGainAudioProcessor(playbackManager, playbackSettings)
}
private var volume = 1f
set(value) {
field = value
@ -57,7 +62,7 @@ constructor(
flush()
}
fun attach() {
init {
playbackManager.addListener(this)
playbackSettings.registerListener(this)
}

View file

@ -86,10 +86,9 @@ class ExoPlaybackStateHolder(
var sessionOngoing = false
private set
fun attach() {
init {
imageSettings.registerListener(this)
player.addListener(this)
replayGainProcessor.attach()
playbackManager.registerStateHolder(this)
playbackSettings.registerListener(this)
musicRepository.addUpdateListener(this)
@ -582,13 +581,14 @@ class ExoPlaybackStateHolder(
private val playbackSettings: PlaybackSettings,
private val commandFactory: PlaybackCommand.Factory,
private val mediaSourceFactory: MediaSource.Factory,
private val replayGainProcessor: ReplayGainAudioProcessor,
private val replayGainProcessorFactory: ReplayGainAudioProcessor.Factory,
private val musicRepository: MusicRepository,
private val imageSettings: ImageSettings,
) {
fun create(): ExoPlaybackStateHolder {
// Since Auxio is a music player, only specify an audio renderer to save
// battery/apk size/cache size
val replayGainProcessor = replayGainProcessorFactory.create()
val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ ->
arrayOf(
FfmpegAudioRenderer(handler, audioListener, replayGainProcessor),

View file

@ -34,36 +34,38 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.widgets.WidgetComponent
class PlaybackServiceFragment
@Inject
constructor(
@ApplicationContext private val context: Context,
class PlaybackServiceFragment private constructor(
private val context: Context,
private val foregroundListener: ForegroundListener,
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings,
private val sessionHolderFactory: MediaSessionHolder.Factory,
private val widgetComponent: WidgetComponent,
exoHolderFactory: ExoPlaybackStateHolder.Factory
exoHolderFactory: ExoPlaybackStateHolder.Factory,
sessionHolderFactory: MediaSessionHolder.Factory,
widgetComponentFactory: WidgetComponent.Factory,
systemReceiverFactory: SystemPlaybackReceiver.Factory,
) : MediaSessionCompat.Callback(), PlaybackStateManager.Listener {
class Factory @Inject constructor(
private val playbackManager: PlaybackStateManager,
private val exoHolderFactory: ExoPlaybackStateHolder.Factory,
private val sessionHolderFactory: MediaSessionHolder.Factory,
private val widgetComponentFactory: WidgetComponent.Factory,
private val systemReceiverFactory: SystemPlaybackReceiver.Factory,
) {
fun create(context: Context, foregroundListener: ForegroundListener) =
PlaybackServiceFragment(context, foregroundListener, playbackManager, exoHolderFactory, sessionHolderFactory, widgetComponentFactory, systemReceiverFactory)
}
private val waitJob = Job()
private val exoHolder = exoHolderFactory.create()
private var foregroundListener: ForegroundListener? = null
private val sessionHolder = sessionHolderFactory.create(context, foregroundListener)
private val widgetComponent = widgetComponentFactory.create(context)
private val systemReceiver = systemReceiverFactory.create(context)
private lateinit var sessionHolder: MediaSessionHolder
private lateinit var systemReceiver: SystemPlaybackReceiver
val token: MediaSessionCompat.Token get() = sessionHolder.token
// --- MEDIASESSION CALLBACKS ---
@SuppressLint("WrongConstant")
fun attach(listener: ForegroundListener): MediaSessionCompat.Token {
foregroundListener = listener
init {
playbackManager.addListener(this)
exoHolder.attach()
sessionHolder = sessionHolderFactory.create(context, listener)
systemReceiver = SystemPlaybackReceiver(playbackManager, playbackSettings, widgetComponent)
ContextCompat.registerReceiver(
context, systemReceiver, systemReceiver.intentFilter, ContextCompat.RECEIVER_EXPORTED)
widgetComponent.attach()
return sessionHolder.token
}
fun handleTaskRemoved() {
@ -101,10 +103,9 @@ constructor(
sessionHolder.release()
exoHolder.release()
playbackManager.removeListener(this)
foregroundListener = null
}
override fun onSessionEnded() {
foregroundListener?.updateForeground(ForegroundListener.Change.MEDIA_SESSION)
foregroundListener.updateForeground(ForegroundListener.Change.MEDIA_SESSION)
}
}

View file

@ -23,34 +23,36 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
import androidx.core.content.ContextCompat
import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.widgets.WidgetComponent
import org.oxycblt.auxio.widgets.WidgetProvider
import javax.inject.Inject
/**
* A [BroadcastReceiver] for receiving playback-specific [Intent]s from the system that require an
* active [IntentFilter] to be registered.
*/
class SystemPlaybackReceiver(
class SystemPlaybackReceiver private constructor(
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings,
private val widgetComponent: WidgetComponent
) : BroadcastReceiver() {
private var initialHeadsetPlugEventHandled = false
val intentFilter =
IntentFilter().apply {
addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
addAction(AudioManager.ACTION_HEADSET_PLUG)
addAction(PlaybackActions.ACTION_INC_REPEAT_MODE)
addAction(PlaybackActions.ACTION_INVERT_SHUFFLE)
addAction(PlaybackActions.ACTION_SKIP_PREV)
addAction(PlaybackActions.ACTION_PLAY_PAUSE)
addAction(PlaybackActions.ACTION_SKIP_NEXT)
addAction(WidgetProvider.ACTION_WIDGET_UPDATE)
class Factory @Inject constructor(
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings,
private val widgetComponent: WidgetComponent
) {
fun create(context: Context): SystemPlaybackReceiver {
val receiver = SystemPlaybackReceiver(playbackManager, playbackSettings, widgetComponent)
ContextCompat.registerReceiver(context, receiver, INTENT_FILTER, ContextCompat.RECEIVER_EXPORTED)
return receiver
}
}
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
@ -127,4 +129,18 @@ class SystemPlaybackReceiver(
playbackManager.playing(false)
}
}
private companion object {
val INTENT_FILTER =
IntentFilter().apply {
addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
addAction(AudioManager.ACTION_HEADSET_PLUG)
addAction(PlaybackActions.ACTION_INC_REPEAT_MODE)
addAction(PlaybackActions.ACTION_INVERT_SHUFFLE)
addAction(PlaybackActions.ACTION_SKIP_PREV)
addAction(PlaybackActions.ACTION_PLAY_PAUSE)
addAction(PlaybackActions.ACTION_SKIP_NEXT)
addAction(WidgetProvider.ACTION_WIDGET_UPDATE)
}
}
}

View file

@ -46,18 +46,25 @@ import org.oxycblt.auxio.util.logD
*
* @author Alexander Capehart (OxygenCobalt)
*/
class WidgetComponent
@Inject
constructor(
@ApplicationContext private val context: Context,
class WidgetComponent private constructor(
private val context: Context,
private val imageSettings: ImageSettings,
private val bitmapProvider: BitmapProvider,
private val playbackManager: PlaybackStateManager,
private val uiSettings: UISettings
) : PlaybackStateManager.Listener, UISettings.Listener, ImageSettings.Listener {
class Factory @Inject constructor(
private val imageSettings: ImageSettings,
private val bitmapProvider: BitmapProvider,
private val playbackManager: PlaybackStateManager,
private val uiSettings: UISettings
) {
fun create(context: Context) = WidgetComponent(context, imageSettings, bitmapProvider, playbackManager, uiSettings)
}
private val widgetProvider = WidgetProvider()
fun attach() {
init {
playbackManager.addListener(this)
uiSettings.registerListener(this)
imageSettings.registerListener(this)