From 119c260f7fe4cd16a10ec7be1af12a9ee2a31759 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Wed, 18 Oct 2023 15:04:37 -0700 Subject: [PATCH] Cleaned up code and tests --- .../composables/PaywallBackground.kt | 32 +++++++----- .../revenuecatui/composables/RemoteImage.kt | 20 +++----- .../helpers/BlurTransformation.kt | 13 ++--- .../revenuecatui/helpers/BlurBenchmarkTest.kt | 51 +++++++++---------- 4 files changed, 53 insertions(+), 63 deletions(-) diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/PaywallBackground.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/PaywallBackground.kt index d6052a2602..2888dfbbfa 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/PaywallBackground.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/PaywallBackground.kt @@ -20,24 +20,26 @@ import com.revenuecat.purchases.ui.revenuecatui.data.processed.TemplateConfigura import com.revenuecat.purchases.ui.revenuecatui.extensions.conditional import com.revenuecat.purchases.ui.revenuecatui.extensions.defaultBackgroundPlaceholder +// Current implementation uses a transformation on API level < 31, modifier on > 31. @Composable internal fun BoxScope.PaywallBackground(templateConfiguration: TemplateConfiguration) { - // current implementation uses a transformation on API level 30-, modifier on 31+. - val transformation = if (templateConfiguration.configuration.blurredBackgroundImage && Build.VERSION.SDK_INT < 31) + val supportsNativeBlurring = Build.VERSION.SDK_INT >= BackgroundUIConstants.minSDKVersionSupportingBlur + val shouldBlur = templateConfiguration.configuration.blurredBackgroundImage + val imageAlpha = if (shouldBlur) { BackgroundUIConstants.blurAlpha } else 1.0f + + val backwardsCompatibleTransformation = if (shouldBlur && !supportsNativeBlurring) { BlurTransformation( - context = LocalContext.current, radius = BackgroundUIConstants.blurSize.toFloatPx(), - scale = BackgroundUIConstants.blurScale + context = LocalContext.current, + radius = BackgroundUIConstants.blurSize.toFloatPx(), + scale = BackgroundUIConstants.blurScale, ) - else null + } else { + null + } val modifier = Modifier .matchParentSize() - // TODO: try to unify both methods into either a transformation or a modifier - // one notable difference is that the transformation works at the image level so it'd run only once - .conditional( - templateConfiguration.configuration.blurredBackgroundImage - && Build.VERSION.SDK_INT >= 31 - ) { + .conditional(shouldBlur && supportsNativeBlurring) { blur(BackgroundUIConstants.blurSize, edgeTreatment = BlurredEdgeTreatment.Unbounded) } @@ -47,6 +49,7 @@ internal fun BoxScope.PaywallBackground(templateConfiguration: TemplateConfigura painter = painterResource(id = R.drawable.default_background), contentDescription = null, contentScale = BackgroundUIConstants.contentScale, + alpha = imageAlpha ) } else { templateConfiguration.images.backgroundUri?.let { @@ -54,8 +57,8 @@ internal fun BoxScope.PaywallBackground(templateConfiguration: TemplateConfigura urlString = it.toString(), modifier = modifier, contentScale = BackgroundUIConstants.contentScale, - transformation = transformation, - alpha = BackgroundUIConstants.blurAlpha + transformation = backwardsCompatibleTransformation, + alpha = imageAlpha ) } } @@ -63,9 +66,10 @@ internal fun BoxScope.PaywallBackground(templateConfiguration: TemplateConfigura private object BackgroundUIConstants { val blurSize = 40.dp - const val blurAlpha = 0.7f val contentScale = ContentScale.Crop + const val blurAlpha = 0.7f const val blurScale = 0.5f + const val minSDKVersionSupportingBlur = 31 } @Composable diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/RemoteImage.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/RemoteImage.kt index c0dae2b966..38fbd8c794 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/RemoteImage.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/composables/RemoteImage.kt @@ -1,6 +1,5 @@ package com.revenuecat.purchases.ui.revenuecatui.composables -import BlurTransformation import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable @@ -16,6 +15,7 @@ import coil.transform.Transformation import com.revenuecat.purchases.ui.revenuecatui.UIConstant import com.revenuecat.purchases.ui.revenuecatui.helpers.Logger +@SuppressWarnings("LongParameterList") @Composable internal fun RemoteImage( urlString: String, @@ -23,19 +23,14 @@ internal fun RemoteImage( contentScale: ContentScale = ContentScale.Fit, contentDescription: String? = null, transformation: Transformation? = null, - alpha: Float = 1f + alpha: Float = 1f, ) { - - val requestBuilder = ImageRequest.Builder(LocalContext.current) - .data(urlString) - .crossfade(durationMillis = UIConstant.defaultAnimationDurationMillis) - - transformation?.let { - requestBuilder.transformations(listOf(it)) - } - return AsyncImage( - model = requestBuilder.build(), + model = ImageRequest.Builder(LocalContext.current) + .data(urlString) + .crossfade(durationMillis = UIConstant.defaultAnimationDurationMillis) + .transformations(listOfNotNull(transformation)) + .build(), contentDescription = contentDescription, imageLoader = LocalContext.current.getRevenueCatUIImageLoader(), modifier = modifier, @@ -52,7 +47,6 @@ internal fun RemoteImage( ) } - private const val MAX_CACHE_SIZE_BYTES = 25 * 1024 * 1024L // 25 MB @Composable diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurTransformation.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurTransformation.kt index 66fd79a32d..a38c262ec1 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurTransformation.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurTransformation.kt @@ -1,17 +1,12 @@ import android.content.Context import android.graphics.Bitmap -import android.os.Build import android.renderscript.Allocation import android.renderscript.Element import android.renderscript.RenderScript import android.renderscript.ScriptIntrinsicBlur import coil.size.Size import coil.transform.Transformation -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlin.math.abs import kotlin.math.min -import kotlin.math.roundToInt /** * BlurTransformation class applies a blur on a given Bitmap image. @@ -25,7 +20,7 @@ import kotlin.math.roundToInt */ internal class BlurTransformation( private val context: Context, - private val radius: Float = 25f, + private val radius: Float, private val scale: Float = 0.5f, ) : Transformation { @@ -46,12 +41,14 @@ internal class BlurTransformation( override fun hashCode(): Int = radius.hashCode() } +// max radius supported by RenderScript +private const val MAX_SUPPORTED_RADIUS = 25 + internal fun Bitmap.blur(context: Context, radius: Float = 25f): Bitmap { if (radius < 1f) { return this@blur } - // max radius supported by RenderScript is 25 - val updatedRadius = min(radius.toDouble(), 25.toDouble()) + val updatedRadius = min(radius.toDouble(), MAX_SUPPORTED_RADIUS.toDouble()) val rs = RenderScript.create(context) val input = Allocation.createFromBitmap(rs, this) diff --git a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurBenchmarkTest.kt b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurBenchmarkTest.kt index 4689e544b9..d572dd517d 100644 --- a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurBenchmarkTest.kt +++ b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/helpers/BlurBenchmarkTest.kt @@ -8,43 +8,38 @@ import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import blur -import blurByAveraging -import blurUsingRenderScript import kotlinx.coroutines.runBlocking +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class BlurBenchmarkTest { - - class PaywallDataValidationTest { - - @Test - fun `Blur images using native blur`() = runBlocking { - benchmarkBlur("Native Blur") { image, context, radius -> - image.blur(context = context, radius = radius) - } + @Ignore("Test is for checking performance only") + @Test + fun `Blur images using native blur`() = runBlocking { + benchmarkBlur("Native Blur") { image, context, radius -> + image.blur(context = context, radius = radius) } + } - private suspend fun benchmarkBlur(functionName: String, blurFunction: suspend (Bitmap, Context, Float) -> Unit) { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val image = openImage() - val radius = 25f - val iterations = 100 - val startTime = System.currentTimeMillis() - repeat(iterations) { - blurFunction(image, context, radius) - } - val endTime = System.currentTimeMillis() - val totalTime = endTime - startTime - val averageTime = totalTime / iterations - Log.d(TAG, "$functionName:\nTotal time: $totalTime ms, Average time: $averageTime ms") + private suspend fun benchmarkBlur(functionName: String, blurFunction: suspend (Bitmap, Context, Float) -> Unit) { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val image = openImage() + val radius = 25f + val iterations = 100 + val startTime = System.currentTimeMillis() + repeat(iterations) { + blurFunction(image, context, radius) } + val endTime = System.currentTimeMillis() + val totalTime = endTime - startTime + val averageTime = totalTime / iterations + Log.d(TAG, "$functionName:\nTotal time: $totalTime ms, Average time: $averageTime ms") + } - private fun openImage(): Bitmap { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val inputStream = context.assets.open("sample_blurring_image.jpeg") - return BitmapFactory.decodeStream(inputStream) - } + private fun openImage(): Bitmap { + val inputStream = javaClass.classLoader!!.getResource("sample_blurring_image.jpeg").openStream() + return BitmapFactory.decodeStream(inputStream) } }