Skip to content

Commit

Permalink
Paywalls: Support Google fonts and font families with multiple fonts (#…
Browse files Browse the repository at this point in the history
…1338)

### Description
Followup to #1328 

This PR adds support for Google fonts when launching the paywall as an
activity and modifies the API to better support both these and bundled
fonts and font families with multiple fonts.
  • Loading branch information
tonidero committed Oct 31, 2023
1 parent 46b9cf9 commit ddb7979
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 18 deletions.
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ compose-ui = { module = "androidx.compose.ui:ui" }
compose-ui-util = { module = "androidx.compose.ui:ui-util" }
compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
compose-ui-google-fonts = { module = "androidx.compose.ui:ui-text-google-fonts" }
compose-material = { module = "androidx.compose.material:material" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-window-size = { module = "androidx.compose.material3:material3-window-size-class" }
Expand Down
1 change: 1 addition & 0 deletions ui/revenuecatui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ dependencies {
implementation libs.commonmark.strikethrough
implementation libs.activity.compose
implementation libs.androidx.fragment.ktx
implementation libs.compose.ui.google.fonts
debugImplementation libs.compose.ui.tooling
debugImplementation libs.androidx.test.compose.manifest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.core.content.res.ResourcesCompat
import androidx.compose.ui.text.googlefonts.Font
import androidx.compose.ui.text.googlefonts.GoogleFont
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.PurchasesError
import com.revenuecat.purchases.PurchasesErrorCode
Expand All @@ -21,6 +23,8 @@ import com.revenuecat.purchases.ui.revenuecatui.Paywall
import com.revenuecat.purchases.ui.revenuecatui.PaywallListener
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions
import com.revenuecat.purchases.ui.revenuecatui.fonts.FontProvider
import com.revenuecat.purchases.ui.revenuecatui.fonts.GoogleFontProvider
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFont
import com.revenuecat.purchases.ui.revenuecatui.fonts.TypographyType

/**
Expand All @@ -43,10 +47,23 @@ internal class PaywallActivity : ComponentActivity(), PaywallListener {
}

private fun getFontProvider(): FontProvider? {
val fontsMap = getArgs()?.fonts?.mapValues { entry ->
entry.value?.let { fontRes ->
ResourcesCompat.getFont(this, fontRes)?.let { FontFamily(it) }
val googleFontProviders = mutableMapOf<GoogleFontProvider, GoogleFont.Provider>()
val fontsMap = getArgs()?.fonts?.mapValues { (_, fontFamily) ->
val fonts = fontFamily?.fonts?.map { font ->
when (font) {
is PaywallFont.ResourceFont -> Font(font.resourceId, font.fontWeight, font.fontStyle)
is PaywallFont.GoogleFont -> {
val googleFontProvider = font.fontProvider
val provider = googleFontProviders.getOrElse(googleFontProvider) {
val googleProvider = googleFontProvider.toGoogleProvider()
googleFontProviders[googleFontProvider] = googleProvider
googleProvider
}
Font(GoogleFont(font.fontName), provider, font.fontWeight, font.fontStyle)
}
}
}
fonts?.let { FontFamily(it) }
} ?: return null
return object : FontProvider {
override fun getFont(type: TypographyType): FontFamily? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package com.revenuecat.purchases.ui.revenuecatui.activity

import android.os.Parcelable
import com.revenuecat.purchases.ui.revenuecatui.fonts.FontResourceProvider
import com.revenuecat.purchases.ui.revenuecatui.fonts.ParcelizableFontProvider
import com.revenuecat.purchases.ui.revenuecatui.fonts.PaywallFontFamily
import com.revenuecat.purchases.ui.revenuecatui.fonts.TypographyType
import kotlinx.parcelize.Parcelize

@Parcelize
internal data class PaywallActivityArgs(
val offeringId: String? = null,
val fonts: Map<TypographyType, Int?>? = null,
val fonts: Map<TypographyType, PaywallFontFamily?>? = null,
) : Parcelable {
constructor(offeringId: String? = null, fontProvider: FontResourceProvider?) : this(
constructor(offeringId: String? = null, fontProvider: ParcelizableFontProvider?) : this(
offeringId,
fontProvider?.let { TypographyType.values().associateBy({ it }, { fontProvider.getFontResourceId(it) }) },
fontProvider?.let { TypographyType.values().associateBy({ it }, { fontProvider.getFont(it) }) },
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.fragment.app.Fragment
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.Offering
import com.revenuecat.purchases.ui.revenuecatui.fonts.FontResourceProvider
import com.revenuecat.purchases.ui.revenuecatui.fonts.ParcelizableFontProvider
import com.revenuecat.purchases.ui.revenuecatui.helpers.shouldDisplayBlockForEntitlementIdentifier
import com.revenuecat.purchases.ui.revenuecatui.helpers.shouldDisplayPaywall

Expand Down Expand Up @@ -40,47 +40,61 @@ class PaywallActivityLauncher {
/**
* Launch the paywall activity.
* @param offering The offering to be shown in the paywall. If null, the current offering will be shown.
* @param fontProvider The [ParcelizableFontProvider] to be used in the paywall. If null, the default fonts
* will be used.
*/
@JvmOverloads
fun launch(offering: Offering? = null, fontResourceProvider: FontResourceProvider? = null) {
fun launch(offering: Offering? = null, fontProvider: ParcelizableFontProvider? = null) {
activityResultLauncher.launch(
PaywallActivityArgs(
offeringId = offering?.identifier,
fontProvider = fontResourceProvider,
fontProvider = fontProvider,
),
)
}

/**
* Launch the paywall activity if the current user does not have [requiredEntitlementIdentifier] active.
* @param offering The offering to be shown in the paywall. If null, the current offering will be shown.
* @param fontProvider The [ParcelizableFontProvider] to be used in the paywall. If null, the default fonts
* will be used.
* @param requiredEntitlementIdentifier the paywall will be displayed only if the current user does not
* have this entitlement active.
*/
@JvmOverloads
fun launchIfNeeded(
offering: Offering? = null,
fontProvider: ParcelizableFontProvider? = null,
requiredEntitlementIdentifier: String,
) {
launchIfNeeded(
offering = offering,
fontProvider = fontProvider,
shouldDisplayBlock = shouldDisplayBlockForEntitlementIdentifier(requiredEntitlementIdentifier),
)
}

/**
* Launch the paywall activity based on whether the result of [shouldDisplayBlock] is true.
* @param offering The offering to be shown in the paywall. If null, the current offering will be shown.
* @param fontProvider The [ParcelizableFontProvider] to be used in the paywall. If null, the default fonts
* will be used.
* @param shouldDisplayBlock the paywall will be displayed only if this returns true.
*/
@JvmOverloads
fun launchIfNeeded(
offering: Offering? = null,
fontProvider: ParcelizableFontProvider? = null,
shouldDisplayBlock: (CustomerInfo) -> Boolean,
) {
shouldDisplayPaywall(shouldDisplayBlock) { shouldDisplay ->
if (shouldDisplay) {
activityResultLauncher.launch(PaywallActivityArgs(offeringId = offering?.identifier))
activityResultLauncher.launch(
PaywallActivityArgs(
offeringId = offering?.identifier,
fontProvider = fontProvider,
),
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import androidx.compose.ui.text.font.FontFamily

/**
* Class that allows to provide a font family for all text styles.
* @param fontFamily the [FontFamily] to be used for all text styles.
*/
class CustomFontProvider(private val fontFamily: FontFamily) : FontProvider {
override fun getFont(type: TypographyType) = fontFamily
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

/**
* Class that allows to provide a font family for all text styles.
* @param fontFamily the [PaywallFontFamily] to be used for all text styles.
*/
class CustomParcelizableFontProvider(
private val fontFamily: PaywallFontFamily,
) : ParcelizableFontProvider {
override fun getFont(type: TypographyType) = fontFamily
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.ui.text.font.FontFamily
/**
* Implement this interface to provide custom fonts to the [PaywallView]. If you don't, the current material3 theme
* typography will be used.
* If you only want to use a single FontFamily for all text styles use [CustomFontProvider].
* This can't be used when launching the paywall as an activity since the fonts are not parcelable/serializable.
* Use [FontResourceProvider] instead.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import android.os.Parcel
import androidx.compose.ui.text.font.FontStyle
import kotlinx.parcelize.Parceler

internal object FontStyleParceler : Parceler<FontStyle> {
override fun create(parcel: Parcel) = FontStyle(parcel.readInt())

override fun FontStyle.write(parcel: Parcel, flags: Int) {
parcel.writeInt(value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import android.os.Parcel
import androidx.compose.ui.text.font.FontWeight
import kotlinx.parcelize.Parceler

internal object FontWeightParceler : Parceler<FontWeight> {
override fun create(parcel: Parcel) = FontWeight(parcel.readInt())

override fun FontWeight.write(parcel: Parcel, flags: Int) {
parcel.writeInt(weight)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import android.os.Parcelable
import androidx.annotation.ArrayRes
import androidx.compose.ui.text.googlefonts.GoogleFont
import kotlinx.parcelize.Parcelize

/**
* Represents a Google font provider.
*/
@Parcelize
data class GoogleFontProvider(
/**
* The resource ID of the font provider's certificate(s).
*/
@ArrayRes val certificates: Int,
val providerAuthority: String = "com.google.android.gms.fonts",
val providerPackage: String = "com.google.android.gms",
) : Parcelable {

fun toGoogleProvider(): GoogleFont.Provider {
return GoogleFont.Provider(
providerAuthority,
providerPackage,
certificates,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import androidx.annotation.FontRes
import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivityLauncher

/**
* Implement this interface to provide custom fonts to the [PaywallActivityLauncher].
* If you don't, the default material3 theme fonts will be used.
* If you only want to use a single [PaywallFontFamily] for all text styles use [CustomParcelizableFontProvider].
* Use [FontProvider] instead if you are using Compose with [PaywallView] or [PaywallDialog].
*/
interface FontResourceProvider {
interface ParcelizableFontProvider {
/**
* Returns the font resource id to be used for the given [TypographyType]. If null is returned,
* Returns the [PaywallFontFamily] to be used for the given [TypographyType]. If null is returned,
* the default font will be used.
* @param type the [TypographyType] for which the font is being requested.
* @return the font resource id to be used for the given [TypographyType].
* @return the [PaywallFontFamily] to be used for the given [TypographyType].
*/
@FontRes
fun getFontResourceId(type: TypographyType): Int?
fun getFont(type: TypographyType): PaywallFontFamily?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import android.os.Parcelable
import androidx.annotation.FontRes
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.TypeParceler

/**
* Represents a font. You can create either a [GoogleFont] or a [ResourceFont].
*/
sealed class PaywallFont : Parcelable {
/**
* Represents a downloadable Google Font.
*/
@Parcelize
data class GoogleFont(
/**
* Name of the Google font you want to use.
*/
val fontName: String,
/**
* Provider of the Google font.
*/
val fontProvider: GoogleFontProvider,
/**
* The weight of the font. The system uses this to match a font to a font request.
*/
@TypeParceler<FontWeight, FontWeightParceler>()
val fontWeight: FontWeight = FontWeight.Normal,
/**
* The style of the font, normal or italic. The system uses this to match a font to a font request.
*/
@TypeParceler<FontStyle, FontStyleParceler>()
val fontStyle: FontStyle = FontStyle.Normal,
) : PaywallFont()

@Parcelize
data class ResourceFont(
/**
* The resource ID of the font file in font resources.
*/
@FontRes
val resourceId: Int,
/**
* The weight of the font. The system uses this to match a font to a font request.
*/
@TypeParceler<FontWeight, FontWeightParceler>()
val fontWeight: FontWeight = FontWeight.Normal,
/**
* The style of the font, normal or italic. The system uses this to match a font to a font request.
*/
@TypeParceler<FontStyle, FontStyleParceler>()
val fontStyle: FontStyle = FontStyle.Normal,
) : PaywallFont()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.revenuecat.purchases.ui.revenuecatui.fonts

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

/**
* Represents a font family. You can add one ore more [PaywallFont] with different weights and font styles.
*/
@Parcelize
data class PaywallFontFamily(val fonts: List<PaywallFont>) : Parcelable

0 comments on commit ddb7979

Please sign in to comment.