music: move automatic reloading to musikr
This commit is contained in:
parent
ef751f1a11
commit
68098b97ed
5 changed files with 95 additions and 84 deletions
|
@ -48,6 +48,8 @@ interface MusicSettings : Settings<MusicSettings.Listener> {
|
|||
val intelligentSorting: Boolean
|
||||
|
||||
interface Listener {
|
||||
/** Called when the current music locations changed. */
|
||||
fun onMusicLocationsChanged() {}
|
||||
/** Called when a setting controlling how music is loaded has changed. */
|
||||
fun onIndexingSettingChanged() {}
|
||||
/** Called when the [shouldBeObserving] configuration has changed. */
|
||||
|
@ -109,7 +111,10 @@ class MusicSettingsImpl @Inject constructor(@ApplicationContext private val cont
|
|||
// TODO: Differentiate "hard reloads" (Need the cache) and "Soft reloads"
|
||||
// (just need to manipulate data)
|
||||
when (key) {
|
||||
getString(R.string.set_key_music_locations),
|
||||
getString(R.string.set_key_music_locations) -> {
|
||||
L.d("Dispatching music locations change")
|
||||
listener.onMusicLocationsChanged()
|
||||
}
|
||||
getString(R.string.set_key_separators),
|
||||
getString(R.string.set_key_auto_sort_names) -> {
|
||||
L.d("Dispatching indexing setting change for $key")
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.music.MusicSettings
|
|||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.musikr.MusicParent
|
||||
import org.oxycblt.musikr.track.UpdateTracker
|
||||
import timber.log.Timber as L
|
||||
|
||||
class IndexingHolder
|
||||
|
@ -45,7 +46,7 @@ private constructor(
|
|||
private val musicRepository: MusicRepository,
|
||||
private val musicSettings: MusicSettings,
|
||||
private val imageLoader: ImageLoader,
|
||||
private val contentObserver: SystemContentObserver
|
||||
private val updateTracker: UpdateTracker
|
||||
) :
|
||||
MusicRepository.IndexingWorker,
|
||||
MusicRepository.IndexingListener,
|
||||
|
@ -58,7 +59,7 @@ private constructor(
|
|||
private val musicRepository: MusicRepository,
|
||||
private val musicSettings: MusicSettings,
|
||||
private val imageLoader: ImageLoader,
|
||||
private val contentObserver: SystemContentObserver
|
||||
private val updateTracker: UpdateTracker
|
||||
) {
|
||||
fun create(context: Context, listener: ForegroundListener) =
|
||||
IndexingHolder(
|
||||
|
@ -68,7 +69,7 @@ private constructor(
|
|||
musicRepository,
|
||||
musicSettings,
|
||||
imageLoader,
|
||||
contentObserver)
|
||||
updateTracker)
|
||||
}
|
||||
|
||||
private val indexJob = Job()
|
||||
|
@ -87,11 +88,11 @@ private constructor(
|
|||
musicRepository.addUpdateListener(this)
|
||||
musicRepository.addIndexingListener(this)
|
||||
musicRepository.registerWorker(this)
|
||||
contentObserver.attach()
|
||||
updateTracker.track(musicSettings.musicLocations)
|
||||
}
|
||||
|
||||
fun release() {
|
||||
contentObserver.release()
|
||||
updateTracker.release()
|
||||
musicRepository.unregisterWorker(this)
|
||||
musicRepository.removeIndexingListener(this)
|
||||
musicRepository.removeUpdateListener(this)
|
||||
|
@ -163,6 +164,12 @@ private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onMusicLocationsChanged() {
|
||||
super.onMusicLocationsChanged()
|
||||
updateTracker.track(musicSettings.musicLocations)
|
||||
musicRepository.requestIndex(true)
|
||||
}
|
||||
|
||||
override fun onIndexingSettingChanged() {
|
||||
super.onIndexingSettingChanged()
|
||||
musicRepository.requestIndex(true)
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* SystemContentObserver.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.auxio.music.service
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.MediaStore
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
import timber.log.Timber as L
|
||||
|
||||
/**
|
||||
* A [ContentObserver] that observes the [MediaStore] music database for changes, a behavior known
|
||||
* to the user as automatic rescanning. The active (and not passive) nature of observing the
|
||||
* database is what requires [MusicServiceFragment] to stay foreground when this is enabled.
|
||||
*/
|
||||
class SystemContentObserver
|
||||
@Inject
|
||||
constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val musicRepository: MusicRepository,
|
||||
private val musicSettings: MusicSettings
|
||||
) : ContentObserver(Handler(Looper.getMainLooper())), Runnable {
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
fun attach() {
|
||||
context.applicationContext.contentResolver.registerContentObserver(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Release this instance, preventing it from further observing the database and cancelling any
|
||||
* pending update events.
|
||||
*/
|
||||
fun release() {
|
||||
handler.removeCallbacks(this)
|
||||
context.applicationContext.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
// Batch rapid-fire updates to the library into a single call to run after 500ms
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, REINDEX_DELAY_MS)
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
// Check here if we should even start a reindex. This is much less bug-prone than
|
||||
// registering and de-registering this component as this setting changes.
|
||||
if (musicSettings.shouldBeObserving) {
|
||||
L.d("MediaStore changed, starting re-index")
|
||||
musicRepository.requestIndex(true)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val REINDEX_DELAY_MS = 500L
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.oxycblt.musikr.track
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import org.oxycblt.musikr.fs.MusicLocation
|
||||
|
||||
internal class LocationObserver(
|
||||
private val context: Context,
|
||||
private val location: MusicLocation,
|
||||
private val listener: UpdateTracker.Callback
|
||||
) : ContentObserver(Handler(Looper.getMainLooper())), Runnable {
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
init {
|
||||
context.applicationContext.contentResolver.registerContentObserver(
|
||||
location.uri,
|
||||
true,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
fun release() {
|
||||
handler.removeCallbacks(this)
|
||||
context.applicationContext.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
// Batch rapid-fire updates into a single callback after delay
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, REINDEX_DELAY_MS)
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
listener.onUpdate(location)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val REINDEX_DELAY_MS = 500L
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.oxycblt.musikr.track
|
||||
|
||||
import android.content.Context
|
||||
import org.oxycblt.musikr.fs.MusicLocation
|
||||
|
||||
interface UpdateTracker {
|
||||
fun track(locations: List<MusicLocation>)
|
||||
|
||||
fun release()
|
||||
|
||||
interface Callback {
|
||||
fun onUpdate(location: MusicLocation)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(context: Context, callback: Callback): UpdateTracker = UpdateTrackerImpl(context, callback)
|
||||
}
|
||||
}
|
||||
|
||||
private class UpdateTrackerImpl(
|
||||
private val context: Context,
|
||||
private val callback: UpdateTracker.Callback
|
||||
) : UpdateTracker {
|
||||
private val observers = mutableListOf<LocationObserver>()
|
||||
|
||||
override fun track(locations: List<MusicLocation>) {
|
||||
release()
|
||||
observers.addAll(locations.map { LocationObserver(context, it, callback) })
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
observers.forEach { it.release() }
|
||||
observers.clear()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue