[Paywalls V2] Adds support for custom fonts#2090
Conversation
# Conflicts: # ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/components/style/StyleFactory.kt
| @get:JvmSynthetic | ||
| @SerialName("font_name") | ||
| val fontName: String? = null, | ||
| val fontName: FontAlias? = null, |
There was a problem hiding this comment.
Deserializing this as a FontAlias directly allows us to look up fonts in UiConfig.app.fonts in a type safe way.
| @@ -1,5 +1,6 @@ | |||
| package com.revenuecat.purchases.paywalls.components | |||
|
|
|||
| import com.revenuecat.purchases.FontAlias | |||
There was a problem hiding this comment.
This file only contains ripple effect changes caused by changing TextComponent.fontName to be a FontAlias.
| @@ -1,5 +1,6 @@ | |||
| package com.revenuecat.purchases.paywalls.components.common | |||
|
|
|||
| import com.revenuecat.purchases.FontAlias | |||
There was a problem hiding this comment.
This file only contains ripple effect changes caused by changing TextComponent.fontName to be a FontAlias.
| val fontWeight: FontWeight?, | ||
| @get:JvmSynthetic | ||
| val fontFamily: FontFamily?, | ||
| val fontSpec: FontSpec?, |
There was a problem hiding this comment.
This is needed because we can only actually resolve the FontFamily when we know the FontWeight that's applied, which is only known in the TextComponentState/TextComponentView.
| fontSize = 15, | ||
| fontWeight = FontWeight.REGULAR.toFontWeight(), | ||
| fontFamily = null, | ||
| fontSpec = null, |
There was a problem hiding this comment.
This file only contains ripple effect changes caused by changing TextComponentStyle.fontFamily to fontSpec.
| import com.revenuecat.purchases.ui.revenuecatui.components.previewTextComponentStyle | ||
| import com.revenuecat.purchases.ui.revenuecatui.components.properties.ColorStyle | ||
| import com.revenuecat.purchases.ui.revenuecatui.components.properties.ColorStyles | ||
| import com.revenuecat.purchases.ui.revenuecatui.components.properties.FontSpec |
There was a problem hiding this comment.
This file only contains ripple effect changes caused by changing TextComponentStyle.fontFamily to fontSpec.
| .copy( | ||
| fontWeight = fontWeight, | ||
| fontSize = if (applyFontSizeToParagraph) fontSize else style.fontSize, | ||
| fontFamily = fontFamily, |
There was a problem hiding this comment.
FontFamily was not being applied to the style. Not a priority, but I wouldn't mind replacing this Markdown component at some point 😅
There was a problem hiding this comment.
Oops, nice catch! And yeah I wouldn't be opposed at all... We kinda copied some functionality... but I would agree the current code is not the best 😅 .
|
|
||
| import androidx.compose.ui.graphics.Color | ||
| import androidx.compose.ui.graphics.toArgb | ||
| import com.revenuecat.purchases.FontAlias |
There was a problem hiding this comment.
This file only contains ripple effect changes caused by adding a fontAliases map to StyleFactory.
| import androidx.compose.ui.graphics.Color | ||
| import androidx.compose.ui.graphics.toArgb | ||
| import com.revenuecat.purchases.ColorAlias | ||
| import com.revenuecat.purchases.FontAlias |
There was a problem hiding this comment.
This file contains a bunch of ripple effect changes, but also some additional tests.
| @@ -1,12 +1,14 @@ | |||
| package com.revenuecat.purchases.ui.revenuecatui.components | |||
|
|
|||
| import com.revenuecat.purchases.FontAlias | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
| @@ -75,7 +75,7 @@ class ButtonComponentViewTests { | |||
| ), | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
| @@ -39,9 +39,9 @@ import com.revenuecat.purchases.paywalls.components.properties.HorizontalAlignme | |||
| import com.revenuecat.purchases.ui.revenuecatui.components.style.PackageComponentStyle | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
| @@ -48,11 +48,11 @@ import com.revenuecat.purchases.ui.revenuecatui.components.style.StackComponentS | |||
| import com.revenuecat.purchases.ui.revenuecatui.components.style.StyleFactory | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
| @@ -138,6 +138,7 @@ internal class StackComponentViewWindowTests { | |||
| ) | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
| @@ -51,11 +51,11 @@ import com.revenuecat.purchases.ui.revenuecatui.components.style.StyleFactory | |||
| import com.revenuecat.purchases.ui.revenuecatui.components.style.TextComponentStyle | |||
There was a problem hiding this comment.
This file only contains ripple effect changes.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #2090 +/- ##
=======================================
Coverage 81.13% 81.13%
=======================================
Files 268 268
Lines 8932 8932
Branches 1266 1266
=======================================
Hits 7247 7247
Misses 1159 1159
Partials 526 526 ☔ View full report in Codecov by Sentry. |
tonidero
left a comment
There was a problem hiding this comment.
This is great! Thanks for tackling this! Quite a few things to handle with this one 🫶
| .copy( | ||
| fontWeight = fontWeight, | ||
| fontSize = if (applyFontSizeToParagraph) fontSize else style.fontSize, | ||
| fontFamily = fontFamily, |
There was a problem hiding this comment.
Oops, nice catch! And yeah I wouldn't be opposed at all... We kinda copied some functionality... but I would agree the current code is not the best 😅 .
| FontFamily.Cursive.name -> FontSpec.Generic.Cursive | ||
| else -> { | ||
| @SuppressLint("DiscouragedApi") | ||
| val fontId = getResourceIdentifier(name = info.value, type = "font") |
There was a problem hiding this comment.
I've been wondering whether it makes sense for the dashboard to have an option to use either a resource or system font... But I guess that wouldn't work super well with supporting both iOS + Android...
As for the optimization, I guess if we find this is a problem we could also try to further optimize this by caching this information across paywall restarts... (Surviving app restarts would be trickier though) but I think it might be ok with this. We might want to do some performance checks on an older device just to make sure though.
There was a problem hiding this comment.
to have an option to use...
Is this missing a word? 😅 Otherwise, could you clarify what you mean?
Those are indeed good strategies we can use to further optimize! We should definitely do a round of performance checks, but I think that can be done separately.
There was a problem hiding this comment.
right, sorry, I meant to have an option to use either a resource or system font... 🙇. But yeah, I think this is ok for now 👍
**This is an automatic release.** ## RevenueCat SDK ### 🐞 Bugfixes * fix: Add prepaid as a period type (#2141) via Greenie (@greenietea) ## RevenueCatUI SDK ### Paywalls v2 #### ✨ New Features * [Paywalls V2] Adds support for Paywalls V2! (#2127) via JayShortway (@JayShortway) ### Customer Center #### ✨ New Features * Adds support for `CustomerCenter` (#2156) via Cesar de la Vega (@vegaro) ### 🔄 Other Changes * [Paywalls V2] Deprecate PaywallFooter in favor of OriginalTemplatePaywallFooter (#2111) via Toni Rico (@tonidero) * feat: Filter CANCEL from help path if lifetime (#2140) via Facundo Menzella (@facumenzella) * fix: Localize Restore Purchases Dialog (#2139) via Facundo Menzella (@facumenzella) * fix: Hide contact support if there's no email (#2135) via Facundo Menzella (@facumenzella) * feat: Track IMPRESSION and SURVER_OPTION_CHOSEN for CustomerCenter (#2124) via Facundo Menzella (@facumenzella) * fix: Use TextButton instead of outline for Promotionals (#2138) via Facundo Menzella (@facumenzella) * Rename carousel slide to page (#2144) via Josh Holtz (@joshdholtz) * [Paywalls V2] Make border draw on top of the image overlay (#2165) via Toni Rico (@tonidero) * [Paywalls V2] Gradients Reloaded: Makes linear gradients match CSS even more closely (#2166) via JayShortway (@JayShortway) * [Paywalls V2] Fix wrong spacers in Stack when some children are Fill (#2164) via Toni Rico (@tonidero) * chore: Unify Json encoding with JsonHelper (#2160) via Facundo Menzella (@facumenzella) * [Paywalls V2] Fixes the top system bar padding being applied to all children in a vertical container (#2162) via JayShortway (@JayShortway) * [Paywalls V2] Refactor to use a single ImageLoader singleton in RevenueCatUI (#2161) via Toni Rico (@tonidero) * [Paywalls V2] Apply stack margins to overlay badges (#2158) via Toni Rico (@tonidero) * [Paywalls V2] Disables the click handler for the selected package (#2159) via JayShortway (@JayShortway) * [Paywalls V2] Change stack distribution system to use spacers instead of custom arrangements (#2154) via Toni Rico (@tonidero) * [Paywalls V2] Fixes empty stacks not showing up. (#2157) via JayShortway (@JayShortway) * Explicitly sets the `defaults` flavor as the default. (#2155) via JayShortway (@JayShortway) * Updates on no active purchases screen (#2150) via Cesar de la Vega (@vegaro) * [Paywalls V2] Fix issue with sizing of stacks with nested badges (#2152) via Toni Rico (@tonidero) * [Paywalls V2] Fix issue with shadows overlapping long edgeToEdge badges (#2149) via Toni Rico (@tonidero) * [Paywalls V2] Adjusts the convex/concave offset to 10% of the image height (#2151) via JayShortway (@JayShortway) * [Paywalls V2] Fixes variables for multi month periods (#2148) via JayShortway (@JayShortway) * [Paywalls V2] Correctly handles variables for lifetime products (#2145) via JayShortway (@JayShortway) * [Paywalls V2] Add shadows to badge previews (#2147) via Toni Rico (@tonidero) * [Paywalls V2] Predownload paywall low res images (#2143) via Toni Rico (@tonidero) * [Paywalls V2] Makes linear gradients consistent with CSS (#2142) via JayShortway (@JayShortway) * [Paywalls V2] Adds edge-to-edge support (#2137) via JayShortway (@JayShortway) * [Paywalls V2] Make root stack component use all the available space by default (#2136) via Toni Rico (@tonidero) * [Paywalls V2] Avoids a crash if a package is missing (#2130) via JayShortway (@JayShortway) * [Paywalls V2] Fix stack clipping issues (#2126) via Toni Rico (@tonidero) * fix: Address typo and wrong doc for CustomerCenter events (#2133) via Facundo Menzella (@facumenzella) * [Paywalls V2] Fix merge conflict (#2134) via Toni Rico (@tonidero) * [Paywalls V2] Actually ignores the font provider for V2. (#2129) via JayShortway (@JayShortway) * Rename `PromotionalOfferView` to `PromotionalOfferScreen` (#2132) via Cesar de la Vega (@vegaro) * [Paywalls V2] Support background images in StackComponent, CarouselComponent, TabsComponent (#2131) via Toni Rico (@tonidero) * [Paywalls V2] Support scroll orientation in StackComponent (#2108) via Toni Rico (@tonidero) * [Paywalls V2] Correctly selects packages on tabs (#2122) via JayShortway (@JayShortway) * Revamp UI in Customer Center (#2123) via Cesar de la Vega (@vegaro) * [Paywalls V2] Remove cursive as a generic font (#2118) via Josh Holtz (@joshdholtz) * Publishes PaywallsTester to Internal testing track on every commit to `main` (#2100) via JayShortway (@JayShortway) * Fixes `X` in promo offer not dismissing the promo (#2110) via Cesar de la Vega (@vegaro) * [Paywalls V2] Published version of PaywallTester now uses the Paywalls V2 Alpha RC project (#2093) via JayShortway (@JayShortway) * [Paywalls V2] New overrides structure (#2120) via Toni Rico (@tonidero) * [Paywalls V2] Adds `TabsComponent` samples and tests (#2115) via JayShortway (@JayShortway) * Ignores `RestorePurchasesDialog` previews using `IgnoreEmergeSnapshot` (#2116) via Cesar de la Vega (@vegaro) * [Paywalls V2] Adds `TabsComponentView` (#2114) via JayShortway (@JayShortway) * Fixes "View field header collides with a variable" data binding error in PurchaseTester (#2119) via JayShortway (@JayShortway) * feat: Add support for tracking customer center events (#2117) via Facundo Menzella (@facumenzella) * [Paywalls V2] Adds `TabsComponentStyle` (#2113) via JayShortway (@JayShortway) * [Paywalls V2] Adds deserialization of `TabsComponent` (#2101) via JayShortway (@JayShortway) * [Paywalls V2] Implements Variables V2 (#2099) via JayShortway (@JayShortway) * refactor: Introduce EventsManager to track events for different features (#2096) via Facundo Menzella (@facumenzella) * Rename to Web Billing (#2094) via Antonio Borrero Granell (@antoniobg) * [Paywalls V2] Fixes shadows drawing behind transparent components (#2112) via JayShortway (@JayShortway) * Fix title not being reset after closing Feedback Survey (#2109) via Cesar de la Vega (@vegaro) * Support for opening custom urls in Customer Center (#2107) via Cesar de la Vega (@vegaro) * Prices in accept promotional offer button (#2104) via Cesar de la Vega (@vegaro) * Customer Center use remote appearance config (#2102) via Cesar de la Vega (@vegaro) * [Paywalls V2] Use original paywall fallback when trying to use Footer modes in a Components paywall (#2106) via Toni Rico (@tonidero) * [Paywalls V2] Add more `ImageComponentView` preview tests (#2103) via Toni Rico (@tonidero) * [Paywalls V2] Add CarouselComponent page indicator animation (#2105) via Toni Rico (@tonidero) * [Paywalls V2] Add `CarouselComponentView` (#2095) via Toni Rico (@tonidero) * [Paywalls V2] Add `CarouselComponent` infrastructure (#2092) via Toni Rico (@tonidero) * [Paywalls V2] Adds new price calculations needed for Variables V2 (#2098) via JayShortway (@JayShortway) * [Paywalls V2] Preparation for Variables V2 (#2097) via JayShortway (@JayShortway) * Promotional offers (#2011) via Cesar de la Vega (@vegaro) * [Paywalls V2] No longer provides the entire `UiConfig` to `StyleFactory` (#2091) via JayShortway (@JayShortway) * [Paywalls V2] Adds support for custom fonts (#2090) via JayShortway (@JayShortway) * feat: Add basic customer center events (#2075) via Facundo Menzella (@facumenzella) * Add support to manage non-Google purchases in Customer Center (#2067) via Cesar de la Vega (@vegaro) * [Paywalls V2] Add `TimelineComponentView` (#2083) via Toni Rico (@tonidero) * [Paywalls V2] Process `TimelineComponent` overrides and state (#2082) via Toni Rico (@tonidero) * [Paywalls V2] Cleans up after implementing color aliases (#2087) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for backgrounds (#2086) via JayShortway (@JayShortway) * `PaywallTesterApp` is no longer wrapped in a `Surface` (#2089) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for shadows (#2085) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for borders (#2084) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for `TextComponent` (#2080) via JayShortway (@JayShortway) * [Paywalls V2] Add `TimelineComponent` network parsing (#2047) via Toni Rico (@tonidero) * [Paywalls V2] Implements color aliases for `ImageComponent` (#2079) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for `IconComponent` (#2078) via JayShortway (@JayShortway) * Build `SubscriptionDetailsView` using `CustomerInfo` (#2057) via Cesar de la Vega (@vegaro) * Some Material 3 updates to the `ManageSubscriptionsView` (#2072) via JayShortway (@JayShortway) * [Paywalls V2] Implements color aliases for `StackComponent` (#2076) via JayShortway (@JayShortway) * Fixes compilation of `IconComponentView`. (#2073) via JayShortway (@JayShortway) * [Paywalls V2] Make `edgeToEdge` top/bottom badge extend to entire stack background (#2070) via Toni Rico (@tonidero) * [Paywalls V2] Parses `UiConfig` (#2068) via JayShortway (@JayShortway) * [Paywalls V2] Add `IconComponent` (#2071) via Toni Rico (@tonidero) * [Paywalls V2] Badge: Handle main stack border width correctly in overlay and nested badge styles (#2069) via Toni Rico (@tonidero) * [Paywalls V2] Add `edgeToEdge` badge trailing/leading style layout (#2054) via Toni Rico (@tonidero) * [Paywalls V2] Adds support for fallback components (#2064) via JayShortway (@JayShortway) --------- Co-authored-by: revenuecat-ops <ops@revenuecat.com> Co-authored-by: Toni Rico <toni.rico.diez@revenuecat.com> Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com> Co-authored-by: Cesar de la Vega <cesarvegaro@gmail.com>
Description
Lots of files and lines changed, but the complexity is quite contained. I've added comments to files that only contain "ripple effect changes". A lot of lines in the diff are caused by tests.
This adds support for custom fonts, both local and Google Fonts. It's the Android equivalent of RevenueCat/purchases-ios#4631.
FontSpec
It introduces the concept of a
FontSpec. This is an intermediately-resolved font. Why intermediate?FontInfo.Nameis a system font or a local font. This allows us to look up the resources for these local fonts in an optimized way, only once for each font.FontWeightwill apply, as the overrides can influence that. Final resolution happens inTextComponentState.How does it work?
TextComponents have an optionalFontAliasfield.UiConfigcontains a map ofFontAliastoFontInfo(wrapped in aFontsConfig).OfferingToStateMapperwill convert this map to a map ofFontAliastoFontSpec, and passes it toStyleFactory.StyleFactorywill look up allFontAliases declared in everyTextComponent, and accumulate errors for any that are not found. The resultingTextComponentStylehas aFontSpecfield.TextComponentStateresolves theFontSpecif it's non-null, and provide the resultingFontFamilyto theTextComponentView.