Coverage Summary for Class: InAppGlideImageLoaderImpl (cloud.mindbox.mobile_sdk.inapp.data.managers)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| InAppGlideImageLoaderImpl |
100%
(5/5)
|
|
100%
(21/21)
|
100%
(136/136)
|
| InAppGlideImageLoaderImpl$buildRequestListener$1 |
100%
(3/3)
|
66.7%
(4/6)
|
100%
(17/17)
|
99.4%
(157/158)
|
| InAppGlideImageLoaderImpl$loadImage$1 |
|
| InAppGlideImageLoaderImpl$loadImage$2 |
100%
(1/1)
|
|
100%
(4/4)
|
100%
(40/40)
|
| InAppGlideImageLoaderImpl$loadImage$2$1$1 |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(5/5)
|
| Total |
100%
(10/10)
|
66.7%
(4/6)
|
100%
(43/43)
|
99.7%
(338/339)
|
package cloud.mindbox.mobile_sdk.inapp.data.managers
import android.content.Context
import android.graphics.drawable.Drawable
import androidx.core.graphics.drawable.toBitmap
import cloud.mindbox.mobile_sdk.R
import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.InAppImageLoader
import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.InAppImageSizeStorage
import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppContentFetchingError
import cloud.mindbox.mobile_sdk.logger.mindboxLogE
import cloud.mindbox.mobile_sdk.logger.mindboxLogI
import cloud.mindbox.mobile_sdk.maxScreenDimension
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.time.Duration.Companion.milliseconds
internal class InAppGlideImageLoaderImpl(
private val context: Context,
private val inAppImageSizeStorage: InAppImageSizeStorage
) : InAppImageLoader {
private val requests = ConcurrentHashMap<String, Target<Drawable>>()
override suspend fun loadImage(inAppId: String, url: String): Boolean {
mindboxLogI("Loading image for inapp with id $inAppId started")
val timeoutMs = context.getString(R.string.mindbox_inapp_fetching_timeout).toLong()
val maxDim = context.maxScreenDimension()
return try {
withTimeout(timeoutMs.milliseconds) {
suspendCancellableCoroutine { continuation ->
requests[inAppId] = startPreload(inAppId, url, maxDim, timeoutMs.toInt(), continuation)
continuation.invokeOnCancellation { cancelLoading(inAppId) }
}
}
} catch (e: TimeoutCancellationException) {
mindboxLogE("Image loading timed out after ${timeoutMs}ms for inapp $inAppId", e)
throw InAppContentFetchingError(e)
}
}
private fun startPreload(
inAppId: String,
url: String,
maxDim: Int,
timeoutMs: Int,
continuation: CancellableContinuation<Boolean>,
): Target<Drawable> = Glide.with(context)
.load(url)
.timeout(timeoutMs)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.override(maxDim, maxDim)
.centerInside()
.listener(buildRequestListener(inAppId, url, continuation))
.preload(maxDim, maxDim)
private fun buildRequestListener(
inAppId: String,
url: String,
continuation: CancellableContinuation<Boolean>,
): RequestListener<Drawable> = object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean,
): Boolean {
mindboxLogI("Image loading failed for inapp $inAppId, url = $url")
requests.remove(inAppId)
if (continuation.isActive) {
continuation.resumeWithException(InAppContentFetchingError(e))
}
return true
}
override fun onResourceReady(
resource: Drawable,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean,
): Boolean {
mindboxLogI("Image loading succeeded for inapp $inAppId, url = $url")
if (!continuation.isActive) return true
requests.remove(inAppId)
return runCatching {
val bitmap = resource.toBitmap()
inAppImageSizeStorage.addSize(inAppId, url, bitmap.width, bitmap.height)
continuation.resume(true)
}.onFailure { e ->
mindboxLogE("Failed to process loaded image for inapp $inAppId", e)
continuation.resumeWithException(InAppContentFetchingError(null))
}.isSuccess
}
}
override fun cancelLoading(inAppId: String) {
Glide.with(context).clear(requests[inAppId])
requests.remove(inAppId)
}
}