diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt index 7e242479..dfeb5286 100644 --- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt +++ b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt @@ -22,8 +22,9 @@ class CoverArtRequestHandler(private val apiClient: SubsonicAPIClient) : Request override fun load(request: Request, networkPolicy: Int): Result { val id = request.uri.getQueryParameter(QUERY_ID) ?: throw IllegalArgumentException("Nullable id") + val size = request.uri.getQueryParameter(SIZE)?.toLong() - val response = apiClient.getCoverArt(id) + val response = apiClient.getCoverArt(id, size) if (response.hasError() || response.stream == null) { throw IOException("${response.apiError}") } else { diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt index 9cc6799a..78ae0b95 100644 --- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt +++ b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt @@ -7,16 +7,24 @@ internal const val AUTHORITY = BuildConfig.LIBRARY_PACKAGE_NAME internal const val COVER_ART_PATH = "cover_art" internal const val AVATAR_PATH = "avatar" internal const val QUERY_ID = "id" +internal const val SIZE = "size" internal const val QUERY_USERNAME = "username" -internal fun createLoadCoverArtRequest(entityId: String): Uri = Uri.Builder() +/** + * Picasso.load() only accepts an URI as parameter. Therefore we create a bogus URI, in which + * we encode the data that we need in the RequestHandler. + */ +internal fun createLoadCoverArtRequest(config: ImageRequest.CoverArt): Uri = + Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .appendPath(COVER_ART_PATH) - .appendQueryParameter(QUERY_ID, entityId) + .appendQueryParameter(QUERY_ID, config.entityId) + .appendQueryParameter(SIZE, config.size.toString()) .build() -internal fun createLoadAvatarRequest(username: String): Uri = Uri.Builder() +internal fun createLoadAvatarRequest(username: String): Uri = + Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .appendPath(AVATAR_PATH) diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt index 630bbc4a..7fa27f84 100644 --- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt +++ b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt @@ -6,6 +6,9 @@ import com.squareup.picasso.Picasso import com.squareup.picasso.RequestCreator import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient +// TODO: Caching doesn't work as expected because our query string varies. +// Need to use .stableKey() method + class SubsonicImageLoader( context: Context, apiClient: SubsonicAPIClient @@ -13,7 +16,7 @@ class SubsonicImageLoader( private val picasso = Picasso.Builder(context) .addRequestHandler(CoverArtRequestHandler(apiClient)) .addRequestHandler(AvatarRequestHandler(apiClient)) - .build().apply { setIndicatorsEnabled(BuildConfig.DEBUG) } + .build().apply { setIndicatorsEnabled(true) } fun load(request: ImageRequest) = when (request) { is ImageRequest.CoverArt -> loadCoverArt(request) @@ -21,9 +24,10 @@ class SubsonicImageLoader( } private fun loadCoverArt(request: ImageRequest.CoverArt) { - picasso.load(createLoadCoverArtRequest(request.entityId)) + picasso.load(createLoadCoverArtRequest(request)) .addPlaceholder(request) .addError(request) + .stableKey("${request.entityId}-${request.size}" ) .into(request.imageView) } @@ -31,6 +35,7 @@ class SubsonicImageLoader( picasso.load(createLoadAvatarRequest(request.username)) .addPlaceholder(request) .addError(request) + .stableKey(request.username) .into(request.imageView) } @@ -59,8 +64,9 @@ sealed class ImageRequest( class CoverArt( val entityId: String, imageView: ImageView, + val size: Int, placeHolderDrawableRes: Int? = null, - errorDrawableRes: Int? = null + errorDrawableRes: Int? = null, ) : ImageRequest( placeHolderDrawableRes, errorDrawableRes, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt index 6d0d2b99..2464dfcc 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt @@ -2,12 +2,15 @@ package org.moire.ultrasonic.subsonic import android.view.View import android.widget.ImageView +import androidx.core.content.res.ResourcesCompat import org.moire.ultrasonic.R +import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.subsonic.loader.image.ImageRequest import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader import org.moire.ultrasonic.util.ImageLoader import org.moire.ultrasonic.util.LegacyImageLoader +import org.moire.ultrasonic.util.Util /** * Temporary proxy between new [SubsonicImageLoader] and [ImageLoader] interface and old @@ -19,6 +22,11 @@ class SubsonicImageLoaderProxy( legacyImageLoader: LegacyImageLoader, private val subsonicImageLoader: SubsonicImageLoader ) : ImageLoader by legacyImageLoader { + + private var imageSizeLarge = Util.getMaxDisplayMetric() + private var imageSizeDefault = 0 + + override fun loadImage( view: View?, entry: MusicDirectory.Entry?, @@ -40,16 +48,18 @@ class SubsonicImageLoaderProxy( defaultResourceId: Int ) { val id = entry?.coverArt + var requestedSize = size val unknownImageId = if (defaultResourceId == -1) R.drawable.unknown_album else defaultResourceId - if (id != null && - view != null && - view is ImageView - ) { + if (requestedSize <= 0) { + requestedSize = if (large) imageSizeLarge else imageSizeDefault + } + + if (id != null && id.isNotEmpty() && view is ImageView) { val request = ImageRequest.CoverArt( - id, view, + id, view, requestedSize, placeHolderDrawableRes = unknownImageId, errorDrawableRes = unknownImageId ) @@ -65,10 +75,7 @@ class SubsonicImageLoaderProxy( crossFade: Boolean, highQuality: Boolean ) { - if (username != null && - view != null && - view is ImageView - ) { + if (username != null && username.isNotEmpty() && view is ImageView) { val request = ImageRequest.Avatar( username, view, placeHolderDrawableRes = R.drawable.ic_contact_picture, @@ -77,4 +84,15 @@ class SubsonicImageLoaderProxy( subsonicImageLoader.load(request) } } + + init { + val default = ResourcesCompat.getDrawable( + UApp.applicationContext().resources, R.drawable.unknown_album, null) + + // Determine the density-dependent image sizes by taking the fallback album + // image and querying its size. + if (default != null) { + imageSizeDefault = default.intrinsicHeight + } + } }