mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
extract album colours from the thumbnail off the main thread
This commit is contained in:
@@ -74,7 +74,7 @@ fun AlbumDetailRoute(
|
|||||||
album = album,
|
album = album,
|
||||||
songs = songs,
|
songs = songs,
|
||||||
coverArt = viewModel::coverArt,
|
coverArt = viewModel::coverArt,
|
||||||
artwork = rememberArtworkColors(viewModel.coverArt(album?.coverArt, false)),
|
artwork = rememberArtworkColors(viewModel.coverArt(album?.coverArt, true)),
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
onSongClick = viewModel::play,
|
onSongClick = viewModel::play,
|
||||||
playingSongId = playback.item?.id.takeIf { context?.kind == QueueKind.Album && context.refId == albumId },
|
playingSongId = playback.item?.id.takeIf { context?.kind == QueueKind.Album && context.refId == albumId },
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ import coil3.SingletonImageLoader
|
|||||||
import coil3.compose.LocalPlatformContext
|
import coil3.compose.LocalPlatformContext
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil3.request.SuccessResult
|
import coil3.request.SuccessResult
|
||||||
|
import coil3.request.allowHardware
|
||||||
import coil3.toBitmap
|
import coil3.toBitmap
|
||||||
import com.subtracks.data.model.CoverArtRef
|
import com.subtracks.data.model.CoverArtRef
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
private const val SEED_ART_SIZE_PX = 128
|
||||||
|
|
||||||
data class ArtworkColors(
|
data class ArtworkColors(
|
||||||
val scheme: ColorScheme,
|
val scheme: ColorScheme,
|
||||||
val gradientHigh: Color,
|
val gradientHigh: Color,
|
||||||
@@ -97,16 +101,24 @@ private fun rememberArtworkSeed(ref: CoverArtRef?): State<Int?> {
|
|||||||
value = null
|
value = null
|
||||||
val art = ref ?: return@produceState
|
val art = ref ?: return@produceState
|
||||||
value =
|
value =
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.IO) {
|
||||||
val request =
|
try {
|
||||||
ImageRequest
|
val request =
|
||||||
.Builder(context)
|
ImageRequest
|
||||||
.data(art.url)
|
.Builder(context)
|
||||||
.memoryCacheKey(art.cacheKey)
|
.data(art.url)
|
||||||
.diskCacheKey(art.cacheKey)
|
.memoryCacheKey(art.cacheKey)
|
||||||
.build()
|
.diskCacheKey(art.cacheKey)
|
||||||
val image = (SingletonImageLoader.get(context).execute(request) as? SuccessResult)?.image
|
.size(SEED_ART_SIZE_PX)
|
||||||
image?.let { seedFrom(it.toBitmap()) }
|
.allowHardware(false)
|
||||||
|
.build()
|
||||||
|
val image = (SingletonImageLoader.get(context).execute(request) as? SuccessResult)?.image
|
||||||
|
image?.let { seedFrom(it.toBitmap()) }
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.subtracks.ui.theme
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import coil3.ImageLoader
|
||||||
|
import coil3.SingletonImageLoader
|
||||||
|
import coil3.annotation.DelicateCoilApi
|
||||||
|
import coil3.asImage
|
||||||
|
import coil3.test.FakeImageLoaderEngine
|
||||||
|
import com.subtracks.data.model.CoverArtRef
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.annotation.GraphicsMode
|
||||||
|
|
||||||
|
@OptIn(DelicateCoilApi::class)
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@GraphicsMode(GraphicsMode.Mode.NATIVE)
|
||||||
|
class ArtworkExtractionTest {
|
||||||
|
@get:Rule
|
||||||
|
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
|
private val bitmap =
|
||||||
|
Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888).apply {
|
||||||
|
eraseColor(Color.rgb(30, 120, 200))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun installImageLoader() {
|
||||||
|
SingletonImageLoader.reset()
|
||||||
|
SingletonImageLoader.setUnsafe(
|
||||||
|
ImageLoader
|
||||||
|
.Builder(ApplicationProvider.getApplicationContext<Context>())
|
||||||
|
.components { add(FakeImageLoaderEngine.Builder().intercept("art", bitmap.asImage()).build()) }
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun resetImageLoader() {
|
||||||
|
SingletonImageLoader.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resolvesColorsFromCoverArt() {
|
||||||
|
var colors: ArtworkColors? = null
|
||||||
|
composeRule.setContent {
|
||||||
|
colors = rememberArtworkColors(CoverArtRef(url = "art", cacheKey = "art"))
|
||||||
|
}
|
||||||
|
composeRule.waitUntil(timeoutMillis = 5_000) { colors != null }
|
||||||
|
assertNotNull(colors)
|
||||||
|
assertTrue(colors!!.scheme.background.luminance() < 0.1f)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user