Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
id("dev.testify") version "3.0.0" apply false
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.kotlinAndroid) apply false
alias(libs.plugins.serialization) apply false
Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ serialization_version = "1.6.0"
dropshot_version = "0.5.0"
ksp = "2.0.21-1.0.27"
install_referrer = "2.2"
webkit = "1.14.0"
[libraries]

# SQL
Expand All @@ -53,7 +54,7 @@ revenue_cat = { module = "com.revenuecat.purchases:purchases", version.ref = "re

# Browser
browser = { module = "androidx.browser:browser", version.ref = "browser_version" }

webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
# Compose
compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_version" }
activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity_compose_version" }
Expand Down
1 change: 1 addition & 0 deletions superwall/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ dependencies {

// Browser
implementation(libs.browser)
implementation(libs.webkit)

// Core
implementation(libs.core)
Expand Down
23 changes: 19 additions & 4 deletions superwall/src/main/java/com/superwall/sdk/config/ConfigManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.superwall.sdk.models.triggers.Trigger
import com.superwall.sdk.network.SuperwallAPI
import com.superwall.sdk.network.awaitUntilNetworkExists
import com.superwall.sdk.network.device.DeviceHelper
import com.superwall.sdk.paywall.archive.WebArchiveLibrary
import com.superwall.sdk.paywall.manager.PaywallManager
import com.superwall.sdk.storage.DisableVerboseEvents
import com.superwall.sdk.storage.LatestConfig
Expand Down Expand Up @@ -67,6 +68,7 @@ open class ConfigManager(
private val awaitUtilNetwork: suspend () -> Unit = {
context.awaitUntilNetworkExists()
},
private val webArchiveLibrary: WebArchiveLibrary,
) {
private val CACHE_LIMIT = 1.seconds

Expand Down Expand Up @@ -151,6 +153,7 @@ open class ConfigManager(
}
}
} catch (e: Throwable) {
e.printStackTrace()
// If fetching config fails, default to the cached version
// Note: Only a timeout exception is possible here
oldConfig?.let {
Expand Down Expand Up @@ -198,11 +201,20 @@ open class ConfigManager(
} else {
// If there's no cached enrichment and config refresh is disabled,
// try to fetch with 1 sec timeout or fail.
deviceHelper.getEnrichment(0, 1.seconds)
try {
withTimeout(1.seconds) {
return@withTimeout deviceHelper.getEnrichment(0, 1.seconds)
}
} catch (e: Throwable) {
return@async Either.Failure(e)
}
}
}

val attributesDeferred = ioScope.async { factory.makeSessionDeviceAttributes() }
val attributesDeferred =
ioScope.async {
factory.makeSessionDeviceAttributes()
}

// Await results from both operations
val (result, enriched, attributes) =
Expand All @@ -215,6 +227,7 @@ open class ConfigManager(
@Suppress("UNCHECKED_CAST")
track(InternalSuperwallEvent.DeviceAttributes(attributes as HashMap<String, Any>))
}

val configResult = result as Either<Config, Throwable>
val enrichmentResult = enriched as Either<Enrichment, Throwable>
configResult
Expand Down Expand Up @@ -256,7 +269,9 @@ open class ConfigManager(
}.fold(
onSuccess =
{
ioScope.launch { preloadPaywalls() }
ioScope.launch {
preloadPaywalls()
}
},
onFailure =
{ e ->
Expand Down Expand Up @@ -333,7 +348,7 @@ open class ConfigManager(

// Preloads paywalls.
private suspend fun preloadPaywalls() {
if (!options.paywalls.shouldPreload) return
if (!options.paywalls.shouldPreload || !options.paywalls.shouldArchive) return
preloadAllPaywalls()
}

Expand Down
128 changes: 93 additions & 35 deletions superwall/src/main/java/com/superwall/sdk/config/PaywallPreload.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.superwall.sdk.config

import android.content.Context
import com.superwall.sdk.dependencies.OptionsFactory
import com.superwall.sdk.dependencies.RequestFactory
import com.superwall.sdk.dependencies.RuleAttributesFactory
import com.superwall.sdk.misc.IOScope
import com.superwall.sdk.misc.launchWithTracking
import com.superwall.sdk.models.config.Config
import com.superwall.sdk.models.paywall.CacheKey
import com.superwall.sdk.models.paywall.Paywall
import com.superwall.sdk.models.paywall.PaywallIdentifier
import com.superwall.sdk.models.triggers.Trigger
import com.superwall.sdk.paywall.archive.WebArchiveLibrary
import com.superwall.sdk.paywall.manager.PaywallManager
import com.superwall.sdk.paywall.presentation.rule_logic.javascript.RuleEvaluator
import com.superwall.sdk.paywall.request.ResponseIdentifiers
Expand All @@ -25,8 +28,20 @@ class PaywallPreload(
val storage: LocalStorage,
val assignments: Assignments,
val paywallManager: PaywallManager,
val webArchiveLibrary: WebArchiveLibrary,
) {
val ignoredArchiveUrls =
listOf(
"webflow.com",
"webflow.io",
"builder-templates",
"apple.com",
"templates.superwall.com",
"interceptor.superwallapp.com",
)

interface Factory :
OptionsFactory,
RequestFactory,
RuleAttributesFactory,
RuleEvaluator.Factory
Expand Down Expand Up @@ -57,7 +72,10 @@ class PaywallPreload(
unconfirmedAssignments = assignments.unconfirmedAssignments,
expressionEvaluator = expressionEvaluator,
)
preloadPaywalls(paywallIdentifiers = paywallIds)
preloadPaywalls(
paywallIdentifiers = paywallIds,
paywalls = config.paywalls.filter { it.identifier in paywallIds },
)

currentPreloadingTask = null
}
Expand All @@ -74,49 +92,77 @@ class PaywallPreload(
config,
triggersToPreload.toSet(),
)
preloadPaywalls(triggerPaywallIdentifiers)
preloadPaywalls(
triggerPaywallIdentifiers,
config.paywalls.filter { it.identifier in triggerPaywallIdentifiers },
)
}

// Preloads paywalls referenced by triggers.
private suspend fun preloadPaywalls(paywallIdentifiers: Set<String>) {
private suspend fun preloadPaywalls(
paywallIdentifiers: Set<String>,
paywalls: List<Paywall>,
) {
val webviewExists = webViewExists()

val paywalls =
paywalls
.filter { it.identifier in paywallIdentifiers }
.distinctBy { it.identifier }
.filter {
!ignoredArchiveUrls.any { url -> it.url.value.contains(url) }
}

val shouldArchive = factory.makeSuperwallOptions().paywalls.shouldArchive
val shouldPreload = factory.makeSuperwallOptions().paywalls.shouldPreload

val identifiersToDownload =
if (shouldArchive) paywalls.map { it.identifier } else emptyList()

if (webviewExists) {
scope.launchWithTracking {
// List to hold all the Deferred objects
val tasks = mutableListOf<Deferred<Any>>()

for (identifier in paywallIdentifiers) {
val task =
async {
// Your asynchronous operation
val request =
factory.makePaywallRequest(
eventData = null,
responseIdentifiers =
ResponseIdentifiers(
paywallId = identifier,
experiment = null,
),
overrides = null,
isDebuggerLaunched = false,
presentationSourceType = null,
)
try {
paywallManager.getPaywallView(
request = request,
isForPresentation = true,
isPreloading = true,
delegate = null,
)
} catch (e: Exception) {
// Handle exception
// If archiving is enable, cache the available paywalls first
if (shouldArchive) {
async {
cachePaywallsFromManifest(paywalls.toSet())
}.await()
}
// If preloading is enabled, preload the paywalls after archiving them
if (shouldPreload) {
val tasks = mutableListOf<Deferred<Any>>()
for (identifier in paywallIdentifiers.filter { it !in identifiersToDownload }) {
val task =
async {
// Your asynchronous operation
val request =
factory.makePaywallRequest(
eventData = null,
responseIdentifiers =
ResponseIdentifiers(
paywallId = identifier,
experiment = null,
),
overrides = null,
isDebuggerLaunched = false,
presentationSourceType = null,
)

try {
paywallManager.getPaywallView(
request = request,
isForPresentation = true,
isPreloading = true,
delegate = null,
)
} catch (e: Exception) {
// Handle exception
}
}
}
tasks.add(task)
tasks.add(task)
}
// Await all tasks
tasks.awaitAll()
}
// Await all tasks
tasks.awaitAll()
}
}
}
Expand Down Expand Up @@ -166,4 +212,16 @@ class PaywallPreload(
paywallManager.removePaywallView(it)
}
}

private suspend fun cachePaywallsFromManifest(paywalls: Set<Paywall>) {
paywalls
.distinctBy { it.identifier }
.filter {
!ignoredArchiveUrls.any { url -> it.url.value.contains(url) }
}.map {
scope.async {
webArchiveLibrary.downloadManifest(it.identifier, it.url.value, it.manifest)
}
}.awaitAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class PaywallOptions {
// or ``Superwall/preloadPaywalls(forEvents:)``
var shouldPreload: Boolean = true

var shouldArchive: Boolean = false

// Loads paywall template websites from disk, if available. Defaults to `true`.
//
// When you save a change to your paywall in the Superwall dashboard, a key is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.superwall.sdk.models.events.EventData
import com.superwall.sdk.models.paywall.Paywall
import com.superwall.sdk.models.product.ProductVariable
import com.superwall.sdk.network.Api
import com.superwall.sdk.network.ArchiveService
import com.superwall.sdk.network.BaseHostService
import com.superwall.sdk.network.CollectorService
import com.superwall.sdk.network.EnrichmentService
Expand All @@ -52,6 +53,11 @@ import com.superwall.sdk.network.SubscriptionService
import com.superwall.sdk.network.device.DeviceHelper
import com.superwall.sdk.network.device.DeviceInfo
import com.superwall.sdk.network.session.CustomHttpUrlConnection
import com.superwall.sdk.paywall.archive.Base64ArchiveEncoder
import com.superwall.sdk.paywall.archive.CachedArchiveLibrary
import com.superwall.sdk.paywall.archive.ManifestDownloader
import com.superwall.sdk.paywall.archive.StreamArchiveCompressor
import com.superwall.sdk.paywall.archive.WebArchiveLibrary
import com.superwall.sdk.paywall.manager.PaywallManager
import com.superwall.sdk.paywall.manager.PaywallViewCache
import com.superwall.sdk.paywall.presentation.PaywallInfo
Expand Down Expand Up @@ -154,7 +160,7 @@ class DependencyContainer(
var storeManager: StoreManager
val transactionManager: TransactionManager
val googleBillingWrapper: GoogleBillingWrapper

var archive: WebArchiveLibrary
var entitlements: Entitlements
lateinit var reedemer: WebPaywallRedeemer
private val uiScope
Expand Down Expand Up @@ -274,6 +280,7 @@ class DependencyContainer(
factory = this,
customHttpUrlConnection = httpConnection,
),
archiveService = ArchiveService(httpConnection),
factory = this,
)
errorTracker = ErrorTracker(scope = ioScope, cache = storage)
Expand Down Expand Up @@ -306,13 +313,21 @@ class DependencyContainer(
ioScope,
)

archive =
CachedArchiveLibrary(
storage,
ManifestDownloader(IOScope(), network),
StreamArchiveCompressor(encoder = Base64ArchiveEncoder()),
)

paywallPreload =
PaywallPreload(
factory = this,
storage = storage,
assignments = assignments,
paywallManager = paywallManager,
scope = ioScope,
webArchiveLibrary = archive,
)

configManager =
Expand All @@ -333,6 +348,7 @@ class DependencyContainer(
},
entitlements = entitlements,
webPaywallRedeemer = { reedemer },
webArchiveLibrary = archive,
)

reedemer =
Expand Down Expand Up @@ -753,6 +769,8 @@ class DependencyContainer(

override fun makeSuperwallOptions(): SuperwallOptions = configManager.options

override fun webArchive(): WebArchiveLibrary = archive

override suspend fun makeTriggers(): Set<String> = configManager.triggersByEventName.keys

override suspend fun provideRuleEvaluator(context: Context): ExpressionEvaluating = evaluator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.superwall.sdk.network.Api
import com.superwall.sdk.network.JsonFactory
import com.superwall.sdk.network.device.DeviceHelper
import com.superwall.sdk.network.device.DeviceInfo
import com.superwall.sdk.paywall.archive.WebArchiveLibrary
import com.superwall.sdk.paywall.manager.PaywallViewCache
import com.superwall.sdk.paywall.presentation.internal.PresentationRequest
import com.superwall.sdk.paywall.presentation.internal.PresentationRequestType
Expand Down Expand Up @@ -189,6 +190,8 @@ interface StoreTransactionFactory {

interface OptionsFactory {
fun makeSuperwallOptions(): SuperwallOptions

fun webArchive(): WebArchiveLibrary
}

interface TriggerFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum class LogScope {
paywallView,
nativePurchaseController,
cache,
webarchive,
all,
;

Expand Down
Loading