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:
parent
34217696c2
commit
59df1c3d57
19 changed files with 73 additions and 286 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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,21 +40,10 @@ 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? {
|
||||||
|
|
||||||
fun existing(uri: Uri): MusicLocation?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MusicLocationFactoryImpl
|
|
||||||
@Inject
|
|
||||||
constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val documentPathFactory: DocumentPathFactory
|
|
||||||
) : MusicLocation.Factory {
|
|
||||||
override fun new(uri: Uri): MusicLocation? {
|
|
||||||
if (!DocumentsContract.isTreeUri(uri)) return null
|
if (!DocumentsContract.isTreeUri(uri)) return null
|
||||||
|
val documentPathFactory = DocumentPathFactory.from(context)
|
||||||
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
|
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
|
||||||
val notPersisted =
|
val notPersisted =
|
||||||
context.contentResolverSafe.persistedUriPermissions.none {
|
context.contentResolverSafe.persistedUriPermissions.none {
|
||||||
|
@ -70,7 +57,8 @@ constructor(
|
||||||
return MusicLocation(uri, path)
|
return MusicLocation(uri, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun existing(uri: Uri): MusicLocation? {
|
fun existing(context: Context, uri: Uri): MusicLocation? {
|
||||||
|
val documentPathFactory = DocumentPathFactory.from(context)
|
||||||
if (!DocumentsContract.isTreeUri(uri)) return null
|
if (!DocumentsContract.isTreeUri(uri)) return null
|
||||||
val notPersisted =
|
val notPersisted =
|
||||||
context.contentResolverSafe.persistedUriPermissions.none {
|
context.contentResolverSafe.persistedUriPermissions.none {
|
||||||
|
@ -80,6 +68,7 @@ constructor(
|
||||||
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
|
val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
|
||||||
return MusicLocation(uri, path)
|
return MusicLocation(uri, path)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val VOLUME_INTERNAL = "internal"
|
private const val VOLUME_INTERNAL = "internal"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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>()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
Loading…
Reference in a new issue