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 @AndroidEntryPoint
class AuxioService : class AuxioService :
MediaBrowserServiceCompat(), ForegroundListener, MusicServiceFragment.Invalidator { MediaBrowserServiceCompat(), ForegroundListener, MusicServiceFragment.Invalidator {
@Inject lateinit var playbackFragment: PlaybackServiceFragment @Inject lateinit var playbackFragmentFactory: PlaybackServiceFragment.Factory
private lateinit var playbackFragment: PlaybackServiceFragment
@Inject lateinit var musicFragmentFactory: MusicServiceFragment.Factory @Inject lateinit var musicFragmentFactory: MusicServiceFragment.Factory
lateinit var musicFragment: MusicServiceFragment private lateinit var musicFragment: MusicServiceFragment
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
sessionToken = playbackFragment.attach(this) playbackFragment = playbackFragmentFactory.create(this, this)
sessionToken = playbackFragment.token
musicFragment = musicFragmentFactory.create(this, this, this) musicFragment = musicFragmentFactory.create(this, this, this)
} }

View file

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

View file

@ -86,10 +86,9 @@ class ExoPlaybackStateHolder(
var sessionOngoing = false var sessionOngoing = false
private set private set
fun attach() { init {
imageSettings.registerListener(this) imageSettings.registerListener(this)
player.addListener(this) player.addListener(this)
replayGainProcessor.attach()
playbackManager.registerStateHolder(this) playbackManager.registerStateHolder(this)
playbackSettings.registerListener(this) playbackSettings.registerListener(this)
musicRepository.addUpdateListener(this) musicRepository.addUpdateListener(this)
@ -582,13 +581,14 @@ class ExoPlaybackStateHolder(
private val playbackSettings: PlaybackSettings, private val playbackSettings: PlaybackSettings,
private val commandFactory: PlaybackCommand.Factory, private val commandFactory: PlaybackCommand.Factory,
private val mediaSourceFactory: MediaSource.Factory, private val mediaSourceFactory: MediaSource.Factory,
private val replayGainProcessor: ReplayGainAudioProcessor, private val replayGainProcessorFactory: ReplayGainAudioProcessor.Factory,
private val musicRepository: MusicRepository, private val musicRepository: MusicRepository,
private val imageSettings: ImageSettings, private val imageSettings: ImageSettings,
) { ) {
fun create(): ExoPlaybackStateHolder { fun create(): ExoPlaybackStateHolder {
// Since Auxio is a music player, only specify an audio renderer to save // Since Auxio is a music player, only specify an audio renderer to save
// battery/apk size/cache size // battery/apk size/cache size
val replayGainProcessor = replayGainProcessorFactory.create()
val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ -> val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ ->
arrayOf( arrayOf(
FfmpegAudioRenderer(handler, audioListener, replayGainProcessor), 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.util.logD
import org.oxycblt.auxio.widgets.WidgetComponent import org.oxycblt.auxio.widgets.WidgetComponent
class PlaybackServiceFragment class PlaybackServiceFragment private constructor(
@Inject private val context: Context,
constructor( private val foregroundListener: ForegroundListener,
@ApplicationContext private val context: Context,
private val playbackManager: PlaybackStateManager, private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings, exoHolderFactory: ExoPlaybackStateHolder.Factory,
private val sessionHolderFactory: MediaSessionHolder.Factory, sessionHolderFactory: MediaSessionHolder.Factory,
private val widgetComponent: WidgetComponent, widgetComponentFactory: WidgetComponent.Factory,
exoHolderFactory: ExoPlaybackStateHolder.Factory systemReceiverFactory: SystemPlaybackReceiver.Factory,
) : MediaSessionCompat.Callback(), PlaybackStateManager.Listener { ) : 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 waitJob = Job()
private val exoHolder = exoHolderFactory.create() 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 val token: MediaSessionCompat.Token get() = sessionHolder.token
private lateinit var systemReceiver: SystemPlaybackReceiver
// --- MEDIASESSION CALLBACKS --- // --- MEDIASESSION CALLBACKS ---
@SuppressLint("WrongConstant") init {
fun attach(listener: ForegroundListener): MediaSessionCompat.Token {
foregroundListener = listener
playbackManager.addListener(this) 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() { fun handleTaskRemoved() {
@ -101,10 +103,9 @@ constructor(
sessionHolder.release() sessionHolder.release()
exoHolder.release() exoHolder.release()
playbackManager.removeListener(this) playbackManager.removeListener(this)
foregroundListener = null
} }
override fun onSessionEnded() { 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.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.media.AudioManager import android.media.AudioManager
import androidx.core.content.ContextCompat
import org.oxycblt.auxio.playback.PlaybackSettings import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.widgets.WidgetComponent import org.oxycblt.auxio.widgets.WidgetComponent
import org.oxycblt.auxio.widgets.WidgetProvider import org.oxycblt.auxio.widgets.WidgetProvider
import javax.inject.Inject
/** /**
* A [BroadcastReceiver] for receiving playback-specific [Intent]s from the system that require an * A [BroadcastReceiver] for receiving playback-specific [Intent]s from the system that require an
* active [IntentFilter] to be registered. * active [IntentFilter] to be registered.
*/ */
class SystemPlaybackReceiver( class SystemPlaybackReceiver private constructor(
private val playbackManager: PlaybackStateManager, private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings, private val playbackSettings: PlaybackSettings,
private val widgetComponent: WidgetComponent private val widgetComponent: WidgetComponent
) : BroadcastReceiver() { ) : BroadcastReceiver() {
private var initialHeadsetPlugEventHandled = false private var initialHeadsetPlugEventHandled = false
val intentFilter = class Factory @Inject constructor(
IntentFilter().apply { private val playbackManager: PlaybackStateManager,
addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY) private val playbackSettings: PlaybackSettings,
addAction(AudioManager.ACTION_HEADSET_PLUG) private val widgetComponent: WidgetComponent
addAction(PlaybackActions.ACTION_INC_REPEAT_MODE) ) {
addAction(PlaybackActions.ACTION_INVERT_SHUFFLE) fun create(context: Context): SystemPlaybackReceiver {
addAction(PlaybackActions.ACTION_SKIP_PREV) val receiver = SystemPlaybackReceiver(playbackManager, playbackSettings, widgetComponent)
addAction(PlaybackActions.ACTION_PLAY_PAUSE) ContextCompat.registerReceiver(context, receiver, INTENT_FILTER, ContextCompat.RECEIVER_EXPORTED)
addAction(PlaybackActions.ACTION_SKIP_NEXT) return receiver
addAction(WidgetProvider.ACTION_WIDGET_UPDATE)
} }
}
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
when (intent.action) { when (intent.action) {
@ -127,4 +129,18 @@ class SystemPlaybackReceiver(
playbackManager.playing(false) 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) * @author Alexander Capehart (OxygenCobalt)
*/ */
class WidgetComponent class WidgetComponent private constructor(
@Inject private val context: Context,
constructor(
@ApplicationContext private val context: Context,
private val imageSettings: ImageSettings, private val imageSettings: ImageSettings,
private val bitmapProvider: BitmapProvider, private val bitmapProvider: BitmapProvider,
private val playbackManager: PlaybackStateManager, private val playbackManager: PlaybackStateManager,
private val uiSettings: UISettings private val uiSettings: UISettings
) : PlaybackStateManager.Listener, UISettings.Listener, ImageSettings.Listener { ) : 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() private val widgetProvider = WidgetProvider()
fun attach() { init {
playbackManager.addListener(this) playbackManager.addListener(this)
uiSettings.registerListener(this) uiSettings.registerListener(this)
imageSettings.registerListener(this) imageSettings.registerListener(this)