musikr: start unwinding di use

Musikr is eventually going to be an entirely independent gradle module
with a DI-agnostic API, start removing some of the directives (but not
all since some are kinda thorny to untangle)
This commit is contained in:
Alexander Capehart 2024-12-13 08:32:30 -07:00
parent 34217696c2
commit 59df1c3d57
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
19 changed files with 73 additions and 286 deletions

View file

@ -53,18 +53,14 @@ interface MusicSettings : Settings<MusicSettings.Listener> {
} }
} }
class MusicSettingsImpl class MusicSettingsImpl @Inject constructor(@ApplicationContext private val context: Context) :
@Inject Settings.Impl<MusicSettings.Listener>(context), MusicSettings {
constructor(
@ApplicationContext context: Context,
private val musicLocationFactory: MusicLocation.Factory
) : Settings.Impl<MusicSettings.Listener>(context), MusicSettings {
override var musicLocations: List<MusicLocation> override var musicLocations: List<MusicLocation>
get() { get() {
val dirs = val dirs =
sharedPreferences.getStringSet(getString(R.string.set_key_music_locations), null) sharedPreferences.getStringSet(getString(R.string.set_key_music_locations), null)
?: emptySet() ?: emptySet()
return dirs.mapNotNull { musicLocationFactory.existing(Uri.parse(it)) } return dirs.mapNotNull { MusicLocation.existing(context, Uri.parse(it)) }
} }
set(value) { set(value) {
sharedPreferences.edit { sharedPreferences.edit {

View file

@ -18,10 +18,12 @@
package org.oxycblt.auxio.music package org.oxycblt.auxio.music
import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -49,10 +51,11 @@ import timber.log.Timber as L
class MusicViewModel class MusicViewModel
@Inject @Inject
constructor( constructor(
@ApplicationContext context: Context,
private val listSettings: ListSettings, private val listSettings: ListSettings,
private val musicRepository: MusicRepository, private val musicRepository: MusicRepository
private val externalPlaylistManager: ExternalPlaylistManager
) : ViewModel(), MusicRepository.UpdateListener, MusicRepository.IndexingListener { ) : ViewModel(), MusicRepository.UpdateListener, MusicRepository.IndexingListener {
private val externalPlaylistManager = ExternalPlaylistManager.from(context)
private val _indexingState = MutableStateFlow<IndexingState?>(null) private val _indexingState = MutableStateFlow<IndexingState?>(null)

View file

@ -19,8 +19,6 @@
package org.oxycblt.auxio.music.locations package org.oxycblt.auxio.music.locations
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ContentResolver
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -50,7 +48,6 @@ class MusicSourcesDialog :
ViewBindingMaterialDialogFragment<DialogMusicLocationsBinding>(), LocationAdapter.Listener { ViewBindingMaterialDialogFragment<DialogMusicLocationsBinding>(), LocationAdapter.Listener {
private val locationAdapter = LocationAdapter(this) private val locationAdapter = LocationAdapter(this)
private var openDocumentTreeLauncher: ActivityResultLauncher<Uri?>? = null private var openDocumentTreeLauncher: ActivityResultLauncher<Uri?>? = null
@Inject lateinit var musicLocationFactory: MusicLocation.Factory
@Inject lateinit var musicSettings: MusicSettings @Inject lateinit var musicSettings: MusicSettings
override fun onCreateBinding(inflater: LayoutInflater) = override fun onCreateBinding(inflater: LayoutInflater) =
@ -102,7 +99,7 @@ class MusicSourcesDialog :
val locations = val locations =
savedInstanceState?.getStringArrayList(KEY_PENDING_LOCATIONS)?.mapNotNull { savedInstanceState?.getStringArrayList(KEY_PENDING_LOCATIONS)?.mapNotNull {
musicLocationFactory.existing(Uri.parse(it)) MusicLocation.existing(requireContext(), Uri.parse(it))
} ?: musicSettings.musicLocations } ?: musicSettings.musicLocations
locationAdapter.addAll(locations) locationAdapter.addAll(locations)
@ -126,8 +123,6 @@ class MusicSourcesDialog :
requireBinding().locationsEmpty.isVisible = locationAdapter.locations.isEmpty() requireBinding().locationsEmpty.isVisible = locationAdapter.locations.isEmpty()
} }
@Inject lateinit var contentResolver: ContentResolver
/** /**
* Add a Document Tree [Uri] chosen by the user to the current [MusicLocation]s. * Add a Document Tree [Uri] chosen by the user to the current [MusicLocation]s.
* *
@ -140,11 +135,7 @@ class MusicSourcesDialog :
L.d("No URI given (user closed the dialog)") L.d("No URI given (user closed the dialog)")
return return
} }
val takeFlags: Int = val location = MusicLocation.new(requireContext(), uri)
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(uri, takeFlags)
val location = musicLocationFactory.new(uri)
if (location != null) { if (location != null) {
locationAdapter.add(location) locationAdapter.add(location)

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2023 Auxio Project
* FsModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.fs
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface FsModule {
@Binds
fun musicLocationFactory(
musicLocationFactoryImpl: MusicLocationFactoryImpl
): MusicLocation.Factory
}

View file

@ -22,8 +22,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import org.oxycblt.musikr.fs.path.DocumentPathFactory import org.oxycblt.musikr.fs.path.DocumentPathFactory
import org.oxycblt.musikr.fs.query.contentResolverSafe import org.oxycblt.musikr.fs.query.contentResolverSafe
@ -42,43 +40,34 @@ class MusicLocation internal constructor(val uri: Uri, val path: Path) {
return "$uri=${volumeId}:${path.components.unixString}" return "$uri=${volumeId}:${path.components.unixString}"
} }
interface Factory { companion object {
fun new(uri: Uri): MusicLocation? fun new(context: Context, uri: Uri): MusicLocation? {
if (!DocumentsContract.isTreeUri(uri)) return null
fun existing(uri: Uri): MusicLocation? val documentPathFactory = DocumentPathFactory.from(context)
} val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
} val notPersisted =
context.contentResolverSafe.persistedUriPermissions.none {
class MusicLocationFactoryImpl it.uri == uri && it.isReadPermission && it.isWritePermission
@Inject }
constructor( if (notPersisted) {
@ApplicationContext private val context: Context, context.contentResolverSafe.takePersistableUriPermission(
private val documentPathFactory: DocumentPathFactory uri,
) : MusicLocation.Factory { Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
override fun new(uri: Uri): MusicLocation? {
if (!DocumentsContract.isTreeUri(uri)) return null
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
val notPersisted =
context.contentResolverSafe.persistedUriPermissions.none {
it.uri == uri && it.isReadPermission && it.isWritePermission
} }
if (notPersisted) { return MusicLocation(uri, path)
context.contentResolverSafe.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
} }
return MusicLocation(uri, path)
}
override fun existing(uri: Uri): MusicLocation? { fun existing(context: Context, uri: Uri): MusicLocation? {
if (!DocumentsContract.isTreeUri(uri)) return null val documentPathFactory = DocumentPathFactory.from(context)
val notPersisted = if (!DocumentsContract.isTreeUri(uri)) return null
context.contentResolverSafe.persistedUriPermissions.none { val notPersisted =
it.uri == uri && it.isReadPermission && it.isWritePermission context.contentResolverSafe.persistedUriPermissions.none {
} it.uri == uri && it.isReadPermission && it.isWritePermission
if (notPersisted) return null }
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null if (notPersisted) return null
return MusicLocation(uri, path) val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
return MusicLocation(uri, path)
}
} }
} }

View file

@ -24,7 +24,6 @@ import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File import java.io.File
import javax.inject.Inject
import org.oxycblt.musikr.fs.Components import org.oxycblt.musikr.fs.Components
import org.oxycblt.musikr.fs.Path import org.oxycblt.musikr.fs.Path
import org.oxycblt.musikr.fs.Volume import org.oxycblt.musikr.fs.Volume
@ -68,11 +67,17 @@ interface DocumentPathFactory {
* @return The [Path] instance, or null if the path could not be deserialized. * @return The [Path] instance, or null if the path could not be deserialized.
*/ */
fun fromDocumentId(path: String): Path? fun fromDocumentId(path: String): Path?
companion object {
fun from(context: Context): DocumentPathFactory {
val volumeManager = VolumeManager.from(context)
val pathInterpreter = MediaStorePathInterpreter.Factory.from(volumeManager)
return DocumentPathFactoryImpl(context, volumeManager, pathInterpreter)
}
}
} }
class DocumentPathFactoryImpl private class DocumentPathFactoryImpl(
@Inject
constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val volumeManager: VolumeManager, private val volumeManager: VolumeManager,
private val mediaStorePathInterpreterFactory: MediaStorePathInterpreter.Factory private val mediaStorePathInterpreterFactory: MediaStorePathInterpreter.Factory

View file

@ -1,49 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* PathModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.fs.path
import android.content.Context
import android.os.storage.StorageManager
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import org.oxycblt.auxio.util.getSystemServiceCompat
@Module
@InstallIn(SingletonComponent::class)
class PathModule {
@Provides
fun volumeManager(@ApplicationContext context: Context): VolumeManager =
VolumeManagerImpl(context.getSystemServiceCompat(StorageManager::class))
@Provides
fun mediaStorePathInterpreterFactory(
volumeManager: VolumeManager
): MediaStorePathInterpreter.Factory = MediaStorePathInterpreter.Factory.from(volumeManager)
}
@Module
@InstallIn(SingletonComponent::class)
interface PathBindsModule {
@Binds
fun documentPathFactory(documentTreePathFactory: DocumentPathFactoryImpl): DocumentPathFactory
}

View file

@ -21,7 +21,6 @@ package org.oxycblt.musikr.fs.path
import android.content.Context import android.content.Context
import android.os.storage.StorageManager import android.os.storage.StorageManager
import android.os.storage.StorageVolume import android.os.storage.StorageVolume
import javax.inject.Inject
import org.oxycblt.musikr.fs.Components import org.oxycblt.musikr.fs.Components
import org.oxycblt.musikr.fs.Volume import org.oxycblt.musikr.fs.Volume
@ -40,10 +39,14 @@ interface VolumeManager {
* @see StorageManager.getStorageVolumes * @see StorageManager.getStorageVolumes
*/ */
fun getVolumes(): List<Volume> fun getVolumes(): List<Volume>
companion object {
fun from(context: Context): VolumeManager =
VolumeManagerImpl(context.getSystemService(StorageManager::class.java))
}
} }
class VolumeManagerImpl @Inject constructor(private val storageManager: StorageManager) : private class VolumeManagerImpl(private val storageManager: StorageManager) : VolumeManager {
VolumeManager {
override fun getInternalVolume(): Volume.Internal = override fun getInternalVolume(): Volume.Internal =
InternalVolumeImpl(storageManager.primaryStorageVolume) InternalVolumeImpl(storageManager.primaryStorageVolume)

View file

@ -22,8 +22,6 @@ import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
@ -36,6 +34,10 @@ import org.oxycblt.musikr.fs.Path
interface DeviceFiles { interface DeviceFiles {
fun explore(locations: Flow<MusicLocation>): Flow<DeviceFile> fun explore(locations: Flow<MusicLocation>): Flow<DeviceFile>
companion object {
fun from(context: Context): DeviceFiles = DeviceFilesImpl(context.contentResolverSafe)
}
} }
data class DeviceFile( data class DeviceFile(
@ -47,10 +49,7 @@ data class DeviceFile(
) )
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class DeviceFilesImpl @Inject constructor(@ApplicationContext private val context: Context) : private class DeviceFilesImpl(private val contentResolver: ContentResolver) : DeviceFiles {
DeviceFiles {
private val contentResolver = context.contentResolverSafe
override fun explore(locations: Flow<MusicLocation>): Flow<DeviceFile> = override fun explore(locations: Flow<MusicLocation>): Flow<DeviceFile> =
locations.flatMapMerge { location -> locations.flatMapMerge { location ->
exploreImpl( exploreImpl(

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* QueryModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.fs.query
import android.content.ContentResolver
import android.content.Context
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class QueryProvidesModule {
@Provides
fun contentResolver(@ApplicationContext context: Context): ContentResolver =
context.contentResolverSafe
}
@Module
@InstallIn(SingletonComponent::class)
interface QueryBindsModule {
@Binds fun deviceFiles(deviceFilesImpl: DeviceFilesImpl): DeviceFiles
}

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2023 Auxio Project
* GraphModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.graph
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface GraphModule {
@Binds fun musicGraphFactory(interpreter: MusicGraphFactoryImpl): MusicGraph.Factory
}

View file

@ -18,7 +18,6 @@
package org.oxycblt.musikr.graph package org.oxycblt.musikr.graph
import javax.inject.Inject
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
import org.oxycblt.musikr.Music import org.oxycblt.musikr.Music
import org.oxycblt.musikr.tag.interpret.PreAlbum import org.oxycblt.musikr.tag.interpret.PreAlbum
@ -39,15 +38,11 @@ data class MusicGraph(
fun build(): MusicGraph fun build(): MusicGraph
} }
interface Factory { companion object {
fun builder(): Builder fun builder(): Builder = MusicGraphBuilderImpl()
} }
} }
class MusicGraphFactoryImpl @Inject constructor() : MusicGraph.Factory {
override fun builder(): MusicGraph.Builder = MusicGraphBuilderImpl()
}
private class MusicGraphBuilderImpl : MusicGraph.Builder { private class MusicGraphBuilderImpl : MusicGraph.Builder {
private val songVertices = mutableMapOf<Music.UID, SongVertex>() private val songVertices = mutableMapOf<Music.UID, SongVertex>()
private val albumVertices = mutableMapOf<PreAlbum, AlbumVertex>() private val albumVertices = mutableMapOf<PreAlbum, AlbumVertex>()

View file

@ -43,7 +43,6 @@ class EvaluateStepImpl
@Inject @Inject
constructor( constructor(
private val tagInterpreter: TagInterpreter, private val tagInterpreter: TagInterpreter,
private val musicGraphFactory: MusicGraph.Factory,
private val libraryFactory: LibraryFactory private val libraryFactory: LibraryFactory
) : EvaluateStep { ) : EvaluateStep {
override suspend fun evaluate( override suspend fun evaluate(
@ -56,7 +55,7 @@ constructor(
.map { tagInterpreter.interpret(it.file, it.tags, it.cover, interpretation) } .map { tagInterpreter.interpret(it.file, it.tags, it.cover, interpretation) }
.flowOn(Dispatchers.Main) .flowOn(Dispatchers.Main)
.buffer(Channel.UNLIMITED) .buffer(Channel.UNLIMITED)
val graphBuilder = musicGraphFactory.builder() val graphBuilder = MusicGraph.builder()
preSongs.collect { graphBuilder.add(it) } preSongs.collect { graphBuilder.add(it) }
val graph = graphBuilder.build() val graph = graphBuilder.build()
return libraryFactory.create(graph) return libraryFactory.create(graph)

View file

@ -18,6 +18,8 @@
package org.oxycblt.musikr.pipeline package org.oxycblt.musikr.pipeline
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -33,9 +35,10 @@ interface ExploreStep {
fun explore(locations: List<MusicLocation>): Flow<ExploreNode> fun explore(locations: List<MusicLocation>): Flow<ExploreNode>
} }
class ExploreStepImpl @Inject constructor(private val deviceFiles: DeviceFiles) : ExploreStep { class ExploreStepImpl @Inject constructor(@ApplicationContext private val context: Context) :
ExploreStep {
override fun explore(locations: List<MusicLocation>) = override fun explore(locations: List<MusicLocation>) =
deviceFiles DeviceFiles.from(context)
.explore(locations.asFlow()) .explore(locations.asFlow())
.mapNotNull { .mapNotNull {
when { when {

View file

@ -27,7 +27,6 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import org.oxycblt.ktaglib.KTagLib
import org.oxycblt.musikr.Storage import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.cache.CachedSong import org.oxycblt.musikr.cache.CachedSong
import org.oxycblt.musikr.cover.Cover import org.oxycblt.musikr.cover.Cover
@ -36,7 +35,6 @@ import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.MetadataExtractor import org.oxycblt.musikr.metadata.MetadataExtractor
import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.tag.parse.ParsedTags
import org.oxycblt.musikr.tag.parse.TagParser import org.oxycblt.musikr.tag.parse.TagParser
import timber.log.Timber
interface ExtractStep { interface ExtractStep {
fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic> fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic>

View file

@ -20,8 +20,6 @@ package org.oxycblt.musikr.playlist
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.fs.Components import org.oxycblt.musikr.fs.Components
import org.oxycblt.musikr.fs.Path import org.oxycblt.musikr.fs.Path
@ -56,6 +54,12 @@ interface ExternalPlaylistManager {
* @return True if the playlist was successfully exported, false otherwise. * @return True if the playlist was successfully exported, false otherwise.
*/ */
suspend fun export(playlist: Playlist, uri: Uri, config: ExportConfig): Boolean suspend fun export(playlist: Playlist, uri: Uri, config: ExportConfig): Boolean
companion object {
fun from(context: Context): ExternalPlaylistManager =
ExternalPlaylistManagerImpl(
context, DocumentPathFactory.from(context), M3U.from(context))
}
} }
/** /**
@ -81,10 +85,8 @@ data class ImportedPlaylist(val name: String?, val paths: List<PossiblePaths>)
typealias PossiblePaths = List<Path> typealias PossiblePaths = List<Path>
class ExternalPlaylistManagerImpl class ExternalPlaylistManagerImpl(
@Inject private val context: Context,
constructor(
@ApplicationContext private val context: Context,
private val documentPathFactory: DocumentPathFactory, private val documentPathFactory: DocumentPathFactory,
private val m3u: M3U private val m3u: M3U
) : ExternalPlaylistManager { ) : ExternalPlaylistManager {

View file

@ -17,17 +17,3 @@
*/ */
package org.oxycblt.musikr.playlist package org.oxycblt.musikr.playlist
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface PlaylistModule {
@Binds
fun externalPlaylistManager(
externalPlaylistManager: ExternalPlaylistManagerImpl
): ExternalPlaylistManager
}

View file

@ -75,6 +75,8 @@ interface M3U {
companion object { companion object {
/** The mime type used for M3U files by the android system. */ /** The mime type used for M3U files by the android system. */
const val MIME_TYPE = "audio/x-mpegurl" const val MIME_TYPE = "audio/x-mpegurl"
fun from(context: Context): M3U = M3UImpl(context, VolumeManager.from(context))
} }
} }

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* M3UModule.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.playlist.m3u
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
interface PlaylistModule {
@Binds fun m3u(m3u: M3UImpl): M3U
}