From b55335b7a83e06af0a1b0df97eb2a5bacfa8c959 Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Wed, 6 May 2026 00:04:43 +0530 Subject: [PATCH 1/3] Implement hardware check for image loading Added a check for potential broken hardware in hasPotentialBrokenHardware (not tested) potentially closes #2640 --- .../cloudstream3/utils/ImageModuleCoil.kt | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) 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..9c4a63ce83f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.utils import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri +import android.os.Build import android.os.Build.VERSION.SDK_INT import android.util.Log import android.widget.ImageView @@ -35,14 +36,22 @@ 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") + 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(SDK_INT >= 28 && !hasPotentialBrokenHardware()) // 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) // 10 % of memory for caching .build() } .diskCache { @@ -55,13 +64,12 @@ 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.") + .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 +79,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 +108,6 @@ object ImageLoader { headerBuilder[key] = value } }.build()) - builder() // if passed } } @@ -173,4 +175,4 @@ object ImageLoader { imageData: ByteBuffer?, builder: ImageRequest.Builder.() -> Unit = {} ) = loadImageInternal(imageData = imageData, builder = builder) -} \ No newline at end of file +} From 93f43b095bec4aabba75ceed2c6b03209e7064f8 Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Sat, 16 May 2026 21:24:52 +0530 Subject: [PATCH 2/3] disable hw allocation+ sw fallback --- .../cloudstream3/utils/ImageModuleCoil.kt | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) 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 9c4a63ce83f..8eda17ba025 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt @@ -4,7 +4,6 @@ import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build -import android.os.Build.VERSION.SDK_INT import android.util.Log import android.widget.ImageView import androidx.annotation.DrawableRes @@ -12,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 @@ -23,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 @@ -42,18 +43,22 @@ object ImageLoader { 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 && !hasPotentialBrokenHardware()) // 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) // 10 % of 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()) @@ -63,10 +68,23 @@ object ImageLoader { } /** Pass interceptors with care, unnecessary passing tokens to servers or image hosting services causes unauthorized exceptions **/ - .components { add(OkHttpNetworkFetcherFactory(callFactory = { buildDefaultClient(context) })) } - .apply { - setupCoilLogger() + .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() /** DebugLogger on debug builds which won't slow down release builds & use EventListener for @@ -108,6 +126,7 @@ object ImageLoader { headerBuilder[key] = value } }.build()) + allowHardware(Build.VERSION.SDK_INT >= 28 && !hasPotentialBrokenHardware()) builder() // if passed } } From dd80c367fc4c1f4b5e14477b34b68bc870e8f3c5 Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Sat, 16 May 2026 21:26:56 +0530 Subject: [PATCH 3/3] Comment out allowHardware Comment out allowHardware check for SDK version 28. --- .../java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8eda17ba025..8aa7550d18c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt @@ -126,7 +126,7 @@ object ImageLoader { headerBuilder[key] = value } }.build()) - allowHardware(Build.VERSION.SDK_INT >= 28 && !hasPotentialBrokenHardware()) + //allowHardware(Build.VERSION.SDK_INT >= 28 && !hasPotentialBrokenHardware()) builder() // if passed } }