Scope artwork credentials and preserve library recovery

This commit is contained in:
justinzeus 2026-11-06 01:25:47 +01:00
parent 09f1d72f30
commit 34ea983a51
9 changed files with 263 additions and 11 deletions

View file

@ -1,11 +1,11 @@
package org.justinvisser.wolfskin.design
import android.content.Context
import coil3.ComponentRegistry
import coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.intercept.Interceptor
import coil3.disk.directory
import coil3.imageLoader
import coil3.request.crossfade
import coil3.memory.MemoryCache
import coil3.request.ImageRequest
@ -18,21 +18,43 @@ import kotlinx.coroutines.sync.withPermit
object WolfskinImages {
private var application: Context? = null
private var loader: ImageLoader? = null
private var networkConfiguration: (ComponentRegistry.Builder.() -> Unit)? = null
private const val MEBIBYTE = 1024L * 1024L
private const val IMAGE_READS = 4
private val readPermits = Semaphore(IMAGE_READS)
/** The app supplies authenticated networking without exposing its server/session to design code. */
@Synchronized
fun configureNetwork(context: Context, configure: ComponentRegistry.Builder.() -> Unit) {
val owner = context.applicationContext
if (application === owner && networkConfiguration === configure) return
loader?.shutdown()
loader = null
application = owner
networkConfiguration = configure
}
@Synchronized
fun loader(context: Context): ImageLoader {
val owner = context.applicationContext
if (application !== owner) {
loader?.shutdown()
loader = null
application = owner
networkConfiguration = null
}
if (loader == null) {
val reads = Dispatchers.IO.limitedParallelism(IMAGE_READS)
loader = owner.imageLoader.newBuilder()
// Parent components may attach a Jellyfin token to every HTTP host.
val builder = ImageLoader.Builder(owner)
loader = builder
.memoryCache { MemoryCache.Builder().maxSizeBytes((WolfskinTokens.Image.memoryCacheMB * MEBIBYTE).toLong()).build() }
.diskCache { DiskCache.Builder().directory(owner.cacheDir.resolve("wolfskin-images")).maxSizeBytes((WolfskinTokens.Image.diskCacheMB * MEBIBYTE).toLong()).build() }
.components { add(Interceptor { chain -> readPermits.withPermit { chain.proceed() } }) }
.components {
// Coil replaces this registry; install networking and bounds together.
networkConfiguration?.invoke(this)
add(Interceptor { chain -> readPermits.withPermit { chain.proceed() } })
}
.fetcherCoroutineContext(reads).decoderCoroutineContext(reads).crossfade(false).build()
}
return loader!!