diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ThemeStateHolder.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ThemeStateHolder.kt index 6ece7b7fa..2846195ad 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ThemeStateHolder.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ThemeStateHolder.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject import javax.inject.Singleton -import java.util.concurrent.ConcurrentHashMap @Singleton class ThemeStateHolder @Inject constructor( @@ -139,30 +138,56 @@ class ThemeStateHolder @Inject constructor( 32, 0.75f, true ) { override fun removeEldestEntry(eldest: MutableMap.MutableEntry>?): Boolean { - return size > 30 + return size > 96 } } - private val pendingAlbumColorSchemeRequests = ConcurrentHashMap.newKeySet() + private val emptyAlbumColorScheme = MutableStateFlow(null).asStateFlow() + private val pendingAlbumColorSchemeLock = Any() + private val pendingAlbumColorSchemeTargets = mutableMapOf>>() private fun requestAlbumColorSchemeGeneration( uriString: String, targetFlow: MutableStateFlow ) { - if (!pendingAlbumColorSchemeRequests.add(uriString)) return + if (uriString.isBlank()) return + + val shouldStartRequest = synchronized(pendingAlbumColorSchemeLock) { + val existingTargets = pendingAlbumColorSchemeTargets[uriString] + if (existingTargets != null) { + existingTargets.add(targetFlow) + false + } else { + pendingAlbumColorSchemeTargets[uriString] = mutableSetOf(targetFlow) + true + } + } + + if (!shouldStartRequest) return + + val requestScope = scope + if (requestScope == null) { + synchronized(pendingAlbumColorSchemeLock) { + pendingAlbumColorSchemeTargets.remove(uriString) + } + return + } - scope?.launch(Dispatchers.IO) { + requestScope.launch(Dispatchers.IO) { + var scheme: ColorSchemePair? = null try { - val scheme = colorSchemeProcessor.getOrGenerateColorScheme( + scheme = colorSchemeProcessor.getOrGenerateColorScheme( albumArtUri = uriString, paletteStyle = currentPaletteStyle, colorAccuracyLevel = currentPaletteAccuracy ) - targetFlow.value = scheme } catch (_: Exception) { // Ignore or log } finally { - pendingAlbumColorSchemeRequests.remove(uriString) + val targets = synchronized(pendingAlbumColorSchemeLock) { + pendingAlbumColorSchemeTargets.remove(uriString)?.toList().orEmpty() + } + targets.forEach { it.value = scheme } } } } @@ -171,6 +196,8 @@ class ThemeStateHolder @Inject constructor( uriString: String, eager: Boolean = true ): StateFlow { + if (uriString.isBlank()) return emptyAlbumColorScheme + val existingFlow = individualAlbumColorSchemes[uriString] if (existingFlow != null) { if (eager && existingFlow.value == null) { @@ -190,6 +217,8 @@ class ThemeStateHolder @Inject constructor( } fun ensureAlbumColorScheme(uriString: String) { + if (uriString.isBlank()) return + val targetFlow = individualAlbumColorSchemes[uriString] ?: MutableStateFlow(null).also { individualAlbumColorSchemes[uriString] = it } @@ -276,7 +305,9 @@ class ThemeStateHolder @Inject constructor( level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL || level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE ) { - pendingAlbumColorSchemeRequests.clear() + synchronized(pendingAlbumColorSchemeLock) { + pendingAlbumColorSchemeTargets.clear() + } } }