Skip to content

Commit

Permalink
[Paywalls V2] Improves image previews (#2029)
Browse files Browse the repository at this point in the history
  • Loading branch information
JayShortway authored Jan 8, 2025
1 parent e3fce6c commit 02e87d9
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.window.core.layout.WindowWidthSizeClass
import com.revenuecat.purchases.paywalls.components.properties.ColorScheme
import com.revenuecat.purchases.paywalls.components.properties.ImageUrls
import com.revenuecat.purchases.paywalls.components.properties.Size
import com.revenuecat.purchases.paywalls.components.properties.SizeConstraint
import com.revenuecat.purchases.paywalls.components.properties.SizeConstraint.Fill
import com.revenuecat.purchases.paywalls.components.properties.SizeConstraint.Fit
import com.revenuecat.purchases.paywalls.components.properties.SizeConstraint.Fixed
Expand All @@ -30,6 +31,7 @@ import com.revenuecat.purchases.ui.revenuecatui.components.buildPresentedPartial
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toContentScale
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toLocaleId
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toShape
import com.revenuecat.purchases.ui.revenuecatui.components.modifier.AspectRatio
import com.revenuecat.purchases.ui.revenuecatui.components.style.ImageComponentStyle
import com.revenuecat.purchases.ui.revenuecatui.data.PaywallState

Expand Down Expand Up @@ -117,11 +119,48 @@ internal class ImageComponentState(
if (darkMode) themeImageUrls.dark ?: themeImageUrls.light else themeImageUrls.light
}

private val imageAspectRatio: Float by derivedStateOf {
imageUrls.width.toFloat() / imageUrls.height.toFloat()
}

@get:JvmSynthetic
val size: Size by derivedStateOf {
(presentedPartial?.partial?.size ?: style.size).adjustForImage(imageUrls, density)
}

/**
* Depending on the [size], it's sometimes possible to figure out the aspect ratio of the view before the
* measurement phase. If that is the case, this will be non-null. This is especially helpful when one axis is set
* to Fit.
*/
@get:JvmSynthetic
val aspectRatio: AspectRatio? by derivedStateOf {
with(size) {
when (val height = height) {
is Fit -> when (width) {
is Fit -> AspectRatio(ratio = imageAspectRatio, matchHeightConstraintsFirst = true)
is Fill -> AspectRatio(ratio = imageAspectRatio, matchHeightConstraintsFirst = true)
is Fixed -> null
}

is Fill -> when (width) {
is Fit -> AspectRatio(ratio = imageAspectRatio, matchHeightConstraintsFirst = false)
is Fill -> null
is Fixed -> null
}

is Fixed -> when (val width = width) {
is Fit -> null
is Fill -> null
is Fixed -> AspectRatio(
ratio = width.value.toFloat() / height.value.toFloat(),
matchHeightConstraintsFirst = true,
)
}
}
}
}

@get:JvmSynthetic
val shape: Shape? by derivedStateOf { presentedPartial?.partial?.maskShape?.toShape() ?: style.shape }

Expand Down Expand Up @@ -149,51 +188,49 @@ internal class ImageComponentState(
}

/**
* We don't want to have Fit in any dimension, as that resolves to zero, which results in an invisible image. So
* instead, we use the px size from the provided [ImageUrls], converted to dp.
* Adjusts this size to take into account the size of the image.
*/
private fun Size.adjustForImage(imageUrls: ImageUrls, density: Density): Size =
Size(
width = when (width) {
is Fit -> {
// If height is Fixed, we'll have to scale width by the same factor.
val scaleFactor = when (val height = height) {
is Fit,
is Fill,
-> 1f

is Fixed -> {
val imageHeightDp = with(density) { imageUrls.height.toInt().toDp() }
height.value.toFloat() / imageHeightDp.value
}
}
Fixed(with(density) { (scaleFactor * imageUrls.width.toInt()).toDp().value.toUInt() })
}
width = width.adjustDimension(
other = height,
thisImageDimensionPx = imageUrls.width,
otherImageDimensionPx = imageUrls.height,
density = density,
),
height = height.adjustDimension(
other = width,
thisImageDimensionPx = imageUrls.height,
otherImageDimensionPx = imageUrls.width,
density = density,
),
)

is Fill,
is Fixed,
-> width
},
height = when (height) {
is Fit -> {
// If width is Fixed, we'll have to scale height by the same factor.
val scaleFactor = when (val width = width) {
is Fit,
is Fill,
-> 1f

is Fixed -> {
val imageWidthDp = with(density) { imageUrls.width.toInt().toDp() }
width.value.toFloat() / imageWidthDp.value
}
}

Fixed(with(density) { (scaleFactor * imageUrls.height.toInt()).toDp().value.toUInt() })
/**
* Adjusts this size constraint to take into account the size of the image.
*/
private fun SizeConstraint.adjustDimension(
other: SizeConstraint,
thisImageDimensionPx: UInt,
otherImageDimensionPx: UInt,
density: Density,
): SizeConstraint = when (this) {
is Fit -> {
when (other) {
is Fit -> Fixed(with(density) { thisImageDimensionPx.toInt().toDp().value.toUInt() })
is Fill -> this

is Fixed -> {
// If the other dimension is Fixed, we'll have to scale this one by the same factor.
val otherImageDimensionDp = with(density) { otherImageDimensionPx.toInt().toDp() }
val scaleFactor = other.value.toFloat() / otherImageDimensionDp.value
Fixed(with(density) { (scaleFactor * thisImageDimensionPx.toInt()).toDp().value.toUInt() })
}
}
}

is Fill,
is Fixed,
-> height
},
)
is Fill,
is Fixed,
-> this
}
}
Loading

0 comments on commit 02e87d9

Please sign in to comment.