Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
package com.revenuecat.apitester.kotlin

import com.revenuecat.purchases.DangerousSettings
import com.revenuecat.purchases.InternalRevenueCatAPI

@Suppress("unused", "UNUSED_VARIABLE")
private class DangerousSettingsAPI {
fun check(dangerousSettings: DangerousSettings) {
val autoSync: Boolean = dangerousSettings.autoSyncPurchases
}

@OptIn(InternalRevenueCatAPI::class)
fun checkInternalRevenueCatAPIs() {
val forWorkflows: DangerousSettings = DangerousSettings.forWorkflows()
val forWorkflowsNoSync: DangerousSettings = DangerousSettings.forWorkflows(autoSyncPurchases = false)
val useWorkflows: Boolean = forWorkflows.useWorkflows
}
}
2 changes: 0 additions & 2 deletions purchases/api-defaults-bc7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ package com.revenuecat.purchases {
@dev.drewhamilton.poko.Poko @kotlinx.parcelize.Parcelize public final class DangerousSettings implements android.os.Parcelable {
ctor public DangerousSettings(optional boolean autoSyncPurchases);
method public boolean getAutoSyncPurchases();
method public boolean getUseWorkflows();
property public final boolean autoSyncPurchases;
property public final boolean useWorkflows;
field public static final com.revenuecat.purchases.DangerousSettings.Companion Companion;
}

Expand Down
2 changes: 0 additions & 2 deletions purchases/api-defauts.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ package com.revenuecat.purchases {
@dev.drewhamilton.poko.Poko @kotlinx.parcelize.Parcelize public final class DangerousSettings implements android.os.Parcelable {
ctor public DangerousSettings(optional boolean autoSyncPurchases);
method public boolean getAutoSyncPurchases();
method public boolean getUseWorkflows();
property public final boolean autoSyncPurchases;
property public final boolean useWorkflows;
field public static final com.revenuecat.purchases.DangerousSettings.Companion Companion;
}

Expand Down
2 changes: 0 additions & 2 deletions purchases/api-entitlement.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ package com.revenuecat.purchases {
@dev.drewhamilton.poko.Poko @kotlinx.parcelize.Parcelize public final class DangerousSettings implements android.os.Parcelable {
ctor public DangerousSettings(optional boolean autoSyncPurchases);
method public boolean getAutoSyncPurchases();
method public boolean getUseWorkflows();
property public final boolean autoSyncPurchases;
property public final boolean useWorkflows;
field public static final com.revenuecat.purchases.DangerousSettings.Companion Companion;
}

Expand Down
5 changes: 2 additions & 3 deletions purchases/src/main/baseline-prof.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,11 @@ Lcom/revenuecat/purchases/DangerousSettings;
HSPLcom/revenuecat/purchases/DangerousSettings;-><clinit>()V
HSPLcom/revenuecat/purchases/DangerousSettings;-><init>(Z)V
HSPLcom/revenuecat/purchases/DangerousSettings;-><init>(ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
HSPLcom/revenuecat/purchases/DangerousSettings;-><init>(ZZZZZ)V
HSPLcom/revenuecat/purchases/DangerousSettings;-><init>(ZZZZ)V
HSPLcom/revenuecat/purchases/DangerousSettings;->getApplyObfuscatedAccountIdToSubscriptionChanges$purchases_defaultsBc8Release()Z
HSPLcom/revenuecat/purchases/DangerousSettings;->getAutoSyncPurchases()Z
HSPLcom/revenuecat/purchases/DangerousSettings;->getCustomEntitlementComputation$purchases_defaultsBc8Release()Z
HSPLcom/revenuecat/purchases/DangerousSettings;->getUiPreviewMode$purchases_defaultsBc8Release()Z
HSPLcom/revenuecat/purchases/DangerousSettings;->getUseWorkflows()Z
Lcom/revenuecat/purchases/DangerousSettings$Companion;
HSPLcom/revenuecat/purchases/DangerousSettings$Companion;-><init>()V
HSPLcom/revenuecat/purchases/DangerousSettings$Companion;-><init>(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down Expand Up @@ -2199,4 +2198,4 @@ Lcom/revenuecat/purchases/utils/Result$Success;
Lcom/revenuecat/purchases/utils/SerializationException;
Lcom/revenuecat/purchases/utils/UrlConnectionFactory;
Lcom/revenuecat/purchases/virtualcurrencies/VirtualCurrencyManager;
HSPLcom/revenuecat/purchases/virtualcurrencies/VirtualCurrencyManager;-><init>(Lcom/revenuecat/purchases/identity/IdentityManager;Lcom/revenuecat/purchases/common/caching/DeviceCache;Lcom/revenuecat/purchases/common/Backend;Lcom/revenuecat/purchases/common/AppConfig;)V
HSPLcom/revenuecat/purchases/virtualcurrencies/VirtualCurrencyManager;-><init>(Lcom/revenuecat/purchases/identity/IdentityManager;Lcom/revenuecat/purchases/common/caching/DeviceCache;Lcom/revenuecat/purchases/common/Backend;Lcom/revenuecat/purchases/common/AppConfig;)V
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,12 @@ public class DangerousSettings internal constructor(
internal val uiPreviewMode: Boolean = false,

internal val applyObfuscatedAccountIdToSubscriptionChanges: Boolean = false,

/**
* Enables RevenueCat Workflows (multipage paywalls). Internal RevenueCat use only.
*/
@InternalRevenueCatAPI
public val useWorkflows: Boolean = false,
) : Parcelable {
@OptIn(InternalRevenueCatAPI::class)
public constructor(autoSyncPurchases: Boolean = true) : this(
autoSyncPurchases = autoSyncPurchases,
customEntitlementComputation = false,
uiPreviewMode = false,
applyObfuscatedAccountIdToSubscriptionChanges = false,
useWorkflows = false,
)

public companion object {
Expand All @@ -52,19 +44,5 @@ public class DangerousSettings internal constructor(
uiPreviewMode = true,
applyObfuscatedAccountIdToSubscriptionChanges = false,
)

/**
* Creates a [DangerousSettings] with RevenueCat Workflows (multipage paywalls) enabled.
* Internal RevenueCat use only; behavior may change without warning.
*/
@InternalRevenueCatAPI
@JvmStatic
public fun forWorkflows(autoSyncPurchases: Boolean = true): DangerousSettings = DangerousSettings(
autoSyncPurchases = autoSyncPurchases,
customEntitlementComputation = false,
uiPreviewMode = false,
applyObfuscatedAccountIdToSubscriptionChanges = false,
useWorkflows = true,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ internal class PurchasesFactory(
localeProvider = localeProvider,
)

val workflowsCache = if (appConfig.useWorkflows) WorkflowsCache(deviceCache = cache) else null
val workflowsCache = WorkflowsCache(deviceCache = cache)

val identityManager = IdentityManager(
cache,
Expand Down Expand Up @@ -366,33 +366,31 @@ internal class PurchasesFactory(
fontLoader = fontLoader,
)

val workflowManager = workflowsCache?.let {
WorkflowManager(
backend = backend,
workflowDetailResolver = WorkflowDetailResolver(
workflowCdnFetcher = FileCachedWorkflowCdnFetcher(
// Dedicated FileRepository instance with a concurrency-limited scope, so workflow
// CDN downloads are capped without affecting the instances used for images/video.
fileRepository = DefaultFileRepository(
fileCacheManager = DefaultFileCache(contextForStorage, "rc_compiled_workflows"),
ioScope = CoroutineScope(
Dispatchers.IO.limitedParallelism(MAX_CONCURRENT_WORKFLOW_CDN_FETCHES) +
NonCancellable,
),
val workflowManager = WorkflowManager(
backend = backend,
workflowDetailResolver = WorkflowDetailResolver(
workflowCdnFetcher = FileCachedWorkflowCdnFetcher(
// Dedicated FileRepository instance with a concurrency-limited scope, so workflow
// CDN downloads are capped without affecting the instances used for images/video.
fileRepository = DefaultFileRepository(
fileCacheManager = DefaultFileCache(contextForStorage, "rc_compiled_workflows"),
ioScope = CoroutineScope(
Dispatchers.IO.limitedParallelism(MAX_CONCURRENT_WORKFLOW_CDN_FETCHES) +
NonCancellable,
),
),
),
workflowAssetPreDownloader = WorkflowAssetPreDownloader(
paywallComponentsImagePreDownloader = paywallComponentsImagePreDownloader,
offeringFontPreDownloader = offeringFontPreDownloader,
),
workflowsCache = it,
prefetchDispatcher = Dispatcher(
createConcurrentExecutor(),
runningIntegrationTests = runningIntegrationTests,
),
)
}
),
workflowAssetPreDownloader = WorkflowAssetPreDownloader(
paywallComponentsImagePreDownloader = paywallComponentsImagePreDownloader,
offeringFontPreDownloader = offeringFontPreDownloader,
),
workflowsCache = workflowsCache,
prefetchDispatcher = Dispatcher(
createConcurrentExecutor(),
runningIntegrationTests = runningIntegrationTests,
),
)

val offeringsManager = OfferingsManager(
offeringsCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.revenuecat.purchases.common
import android.content.Context
import com.revenuecat.purchases.APIKeyValidator
import com.revenuecat.purchases.DangerousSettings
import com.revenuecat.purchases.InternalRevenueCatAPI
import com.revenuecat.purchases.PurchasesAreCompletedBy
import com.revenuecat.purchases.Store
import com.revenuecat.purchases.finishTransactions
Expand Down Expand Up @@ -66,10 +65,6 @@ internal class AppConfig(
val applyObfuscatedAccountIdToSubscriptionChanges: Boolean
get() = dangerousSettings.applyObfuscatedAccountIdToSubscriptionChanges

@OptIn(InternalRevenueCatAPI::class)
val useWorkflows: Boolean
get() = dangerousSettings.useWorkflows

val playStoreVersionName = context.playStoreVersionName
val playServicesVersionName = context.playServicesVersionName

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,4 @@ class DangerousSettingsTest {
assertThat(dangerousSettings.customEntitlementComputation).isFalse
assertThat(dangerousSettings.applyObfuscatedAccountIdToSubscriptionChanges).isFalse
}

@OptIn(InternalRevenueCatAPI::class)
@Test
fun `default useWorkflows is false`() {
val dangerousSettings = DangerousSettings()
assertThat(dangerousSettings.useWorkflows).isFalse
}

@OptIn(InternalRevenueCatAPI::class)
@Test
fun `forWorkflows sets useWorkflows to true and leaves other settings at defaults`() {
val dangerousSettings = DangerousSettings.forWorkflows()
assertThat(dangerousSettings.useWorkflows).isTrue
assertThat(dangerousSettings.autoSyncPurchases).isTrue
assertThat(dangerousSettings.customEntitlementComputation).isFalse
assertThat(dangerousSettings.uiPreviewMode).isFalse
assertThat(dangerousSettings.applyObfuscatedAccountIdToSubscriptionChanges).isFalse
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.revenuecat.purchases.APIKeyValidator
import com.revenuecat.purchases.DangerousSettings
import com.revenuecat.purchases.InternalRevenueCatAPI
import com.revenuecat.purchases.PurchasesAreCompletedBy.MY_APP
import com.revenuecat.purchases.PurchasesAreCompletedBy.REVENUECAT
import com.revenuecat.purchases.Store
Expand Down Expand Up @@ -507,8 +506,7 @@ class AppConfigTest {
"autoSyncPurchases=true, " +
"customEntitlementComputation=false, " +
"uiPreviewMode=false, " +
"applyObfuscatedAccountIdToSubscriptionChanges=false, " +
"useWorkflows=false), " +
"applyObfuscatedAccountIdToSubscriptionChanges=false), " +
"languageTag='', " +
"versionName='', " +
"packageName='', " +
Expand Down Expand Up @@ -551,33 +549,4 @@ class AppConfigTest {
}

// endregion Fallback API host

@OptIn(InternalRevenueCatAPI::class)
@Test
fun `useWorkflows reflects dangerousSettings`() {
val enabled = AppConfig(
context = mockk(relaxed = true),
purchasesAreCompletedBy = REVENUECAT,
showInAppMessagesAutomatically = false,
platformInfo = PlatformInfo(flavor = "native", version = "3.2.0"),
proxyURL = null,
store = Store.PLAY_STORE,
isDebugBuild = false,
apiKeyValidationResult = APIKeyValidator.ValidationResult.VALID,
dangerousSettings = DangerousSettings.forWorkflows(),
)
assertThat(enabled.useWorkflows).isTrue

val disabled = AppConfig(
context = mockk(relaxed = true),
purchasesAreCompletedBy = REVENUECAT,
showInAppMessagesAutomatically = false,
platformInfo = PlatformInfo(flavor = "native", version = "3.2.0"),
proxyURL = null,
store = Store.PLAY_STORE,
isDebugBuild = false,
apiKeyValidationResult = APIKeyValidator.ValidationResult.VALID,
)
assertThat(disabled.useWorkflows).isFalse
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ internal class MockPurchasesType(
override val purchasesAreCompletedBy: PurchasesAreCompletedBy = PurchasesAreCompletedBy.REVENUECAT,
override val storefrontCountryCode: String? = null,
override val customerCenterListener: CustomerCenterListener? = null,
override val useWorkflows: Boolean = false,
) : PurchasesType {
override suspend fun awaitPurchase(purchaseParams: PurchaseParams.Builder): PurchaseResult {
throw NotImplementedError("Mock implementation for previews only")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ internal class PaywallViewModelImpl(
private val shouldDisplayBlock: ((CustomerInfo) -> Boolean)?,
preview: Boolean = false,
private val productChangeCalculator: ProductChangeCalculator = ProductChangeCalculator(purchases),
private val useWorkflowsEndpoint: Boolean = purchases.useWorkflows,
private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.Default,
) : ViewModel(), PaywallViewModel {
private val variableDataProvider = VariableDataProvider(resourceProvider, preview)
Expand Down Expand Up @@ -761,12 +760,11 @@ internal class PaywallViewModelImpl(
val resolvedOfferingSelection = resolveOfferingSelection(offeringSelection)
val selectedOffering = resolvedOfferingSelection.selectedOffering

// When workflows are enabled, every non-legacy paywall is served through the /workflows
// endpoint. `offering.paywall == null` is the durable marker of a non-legacy (workflow)
// paywall: a legacy v1 paywall always carries `offering.paywall`, and that field stays
// even after `paywallComponents` is removed and all V2 paywalls move to workflows. We
// deliberately do NOT gate on `paywallComponents`, which is going away.
if (useWorkflowsEndpoint && selectedOffering != null && selectedOffering.paywall == null) {
// Every non-legacy paywall is served through the /workflows endpoint. `offering.paywall == null`
// is the durable marker of a non-legacy (workflow) paywall: a legacy v1 paywall always carries
// `offering.paywall`, and that field stays even after `paywallComponents` is removed and all V2
// paywalls move to workflows. We deliberately do NOT gate on `paywallComponents`, which is going away.
if (selectedOffering != null && selectedOffering.paywall == null) {
presentWorkflow(selectedOffering, resolvedOfferingSelection.offeringsForExitOfferLookup)
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ internal interface PurchasesType {
suspend fun awaitGetWorkflow(workflowId: String): WorkflowDataResult

fun workflowIdForOfferingId(offeringId: String): String?

val useWorkflows: Boolean
}

@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -149,8 +147,4 @@ internal class PurchasesImpl(private val purchases: Purchases = Purchases.shared
@OptIn(InternalRevenueCatAPI::class)
override fun workflowIdForOfferingId(offeringId: String): String? =
purchases.workflowIdForOfferingId(offeringId)

@OptIn(InternalRevenueCatAPI::class)
override val useWorkflows: Boolean
get() = purchases.currentConfiguration.dangerousSettings.useWorkflows
}
Loading