diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt index 9d5c75289c8..8aa7550d18c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt @@ -3,7 +3,7 @@ package com.lagradost.cloudstream3.utils import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri -import android.os.Build.VERSION.SDK_INT +import android.os.Build import android.util.Log import android.widget.ImageView import androidx.annotation.DrawableRes @@ -11,6 +11,7 @@ import coil3.EventListener import coil3.ImageLoader import coil3.PlatformContext import coil3.SingletonImageLoader +import coil3.decode.BitmapFactoryDecoder import coil3.disk.DiskCache import coil3.dispose import coil3.load @@ -22,6 +23,7 @@ import coil3.request.CachePolicy import coil3.request.ErrorResult import coil3.request.ImageRequest import coil3.request.allowHardware +import coil3.request.bitmapConfig import coil3.request.crossfade import coil3.util.DebugLogger import com.lagradost.cloudstream3.BuildConfig @@ -35,16 +37,28 @@ import java.nio.ByteBuffer object ImageLoader { private const val TAG = "CoilImgLoader" - + private fun hasPotentialBrokenHardware(): Boolean { + val hardware = Build.HARDWARE?.lowercase() ?: "" + val board = Build.BOARD?.lowercase() ?: "" + val model = Build.MODEL?.lowercase() ?: "" + val allwinnerPatterns = listOf("sun50iw9", "h713", "allwinner") + val problematicModels = listOf("hy320", "hy300", "a10plus", "magcubic", "sinoy") + Log.e(TAG, "Device signature - Hardware: $hardware, Board: $board, Model: $model") + return allwinnerPatterns.any { it in hardware || it in board } || + problematicModels.any { it in model } + } internal fun buildImageLoader(context: PlatformContext): ImageLoader = ImageLoader.Builder(context) .crossfade(200) - .allowHardware(SDK_INT >= 28) // SDK_INT >= 28, cant use hardware bitmaps for Palette Builder + .allowHardware(false) // SDK_INT >= 28, cant use hardware bitmaps for Palette Builder .diskCachePolicy(CachePolicy.ENABLED) .networkCachePolicy(CachePolicy.ENABLED) .memoryCache { - MemoryCache.Builder().maxSizePercent(context, 0.1) // Use 10 % of the app's available memory for caching + MemoryCache.Builder().maxSizePercent(context, 0.1) + .strongReferencesEnabled(false)// 10 % of memory for caching .build() } + .bitmapConfig(Bitmap.Config.ARGB_8888) + .logger(DebugLogger()) .diskCache { DiskCache.Builder() .directory(context.cacheDir.resolve("cs3_image_cache").toOkioPath()) @@ -54,14 +68,26 @@ object ImageLoader { } /** Pass interceptors with care, unnecessary passing tokens to servers or image hosting services causes unauthorized exceptions **/ - .components { add(OkHttpNetworkFetcherFactory(callFactory = { buildDefaultClient(context) })) } - .also { - it.setupCoilLogger() - Log.d(TAG, "buildImageLoader: Setting COIL Image Loader.") + .components { + add { chain -> + val request = chain.request + Log.d( + TAG, + "Coil request: ${request.data}, allowHardware=${request.allowHardware}" + ) + chain.proceed() + } + add(OkHttpNetworkFetcherFactory(callFactory = { buildDefaultClient(context) })) + //if (hasPotentialBrokenHardware()) { + add(BitmapFactoryDecoder.Factory()) // sw decoder + //} } + /* .apply { + setupCoilLogger() + }*/ .build() - /** Use DebugLogger on debug builds which won't slow down release builds & use EventListener for + /** DebugLogger on debug builds which won't slow down release builds & use EventListener for Errors on release builds. **/ internal fun ImageLoader.Builder.setupCoilLogger() { if (BuildConfig.DEBUG) { @@ -71,33 +97,28 @@ object ImageLoader { eventListener(object : EventListener() { override fun onError(request: ImageRequest, result: ErrorResult) { super.onError(request, result) - Log.e(TAG, "Error loading image: ${result.throwable}") + Log.e(TAG, "Image load error: ${result.throwable.message ?: result.throwable}") + Log.e(TAG, " URL: ${request.data}") + Log.e(TAG, " allowHardware: ${request.allowHardware}") + Log.e(TAG, " hardware: ${Build.HARDWARE}, board: ${Build.BOARD}") } }) - Log.d(TAG, "setupCoilLogger: Activated EVENT_LISTENER FOR COIL") } } - /** we use coil's built in loader with our global synchronized instance, this way we achieve - latest and complete functionality as well as stability **/ + /** coil's built in loader attached w/ global synchronized instance **/ private fun ImageView.loadImageInternal( imageData: Any?, headers: Map? = null, builder: ImageRequest.Builder.() -> Unit = {} // for placeholder, error & transformations ) { - // clear image to avoid loading & flickering issue at fast scrolling (e.g, an image recycler) + // clear image to avoid loading & flickering issue at fast scrolling (~recycler view/lazy column) this.dispose() - - if(imageData == null) return // Just in case - + if (imageData == null) return // setImageResource is better than coil3 on resources due to attr - if(imageData is Int) { - this.setImageResource(imageData) - return - } + if (imageData is Int) { this.setImageResource(imageData); return } - // Use Coil's built-in load method but with our custom module & a decent USER-AGENT always - // which can be overridden by extensions. + // headers can be overridden by extensions. this.load(imageData, SingletonImageLoader.get(context)) { this.httpHeaders(NetworkHeaders.Builder().also { headerBuilder -> headerBuilder["User-Agent"] = USER_AGENT @@ -105,7 +126,7 @@ object ImageLoader { headerBuilder[key] = value } }.build()) - + //allowHardware(Build.VERSION.SDK_INT >= 28 && !hasPotentialBrokenHardware()) builder() // if passed } } @@ -173,4 +194,4 @@ object ImageLoader { imageData: ByteBuffer?, builder: ImageRequest.Builder.() -> Unit = {} ) = loadImageInternal(imageData = imageData, builder = builder) -} \ No newline at end of file +}