diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/BillingFactory.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/BillingFactory.kt index 5ab30c164..5311e6227 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/BillingFactory.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/BillingFactory.kt @@ -19,12 +19,14 @@ internal object BillingFactory { cache: DeviceCache, observerMode: Boolean, diagnosticsTrackerIfEnabled: DiagnosticsTracker?, + stateProvider: PurchasesStateProvider, ) = when (store) { Store.PLAY_STORE -> BillingWrapper( BillingWrapper.ClientFactory(application), Handler(application.mainLooper), cache, diagnosticsTrackerIfEnabled, + stateProvider, ) Store.AMAZON -> { try { @@ -34,6 +36,7 @@ internal object BillingFactory { observerMode, Handler(application.mainLooper), backendHelper, + stateProvider, ) } catch (e: NoClassDefFoundError) { errorLog("Make sure purchases-amazon is added as dependency", e) diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt index df0847467..dbd9b480f 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesFactory.kt @@ -126,6 +126,8 @@ internal class PurchasesFactory( backendHelper, ) + val purchasesStateProvider = PurchasesStateCache(PurchasesState()) + // Override used for integration tests. val billing: BillingAbstract = overrideBillingAbstract ?: BillingFactory.createBilling( store, @@ -134,6 +136,7 @@ internal class PurchasesFactory( cache, observerMode, diagnosticsTracker, + purchasesStateProvider, ) val subscriberAttributesPoster = SubscriberAttributesPoster(backendHelper) @@ -268,6 +271,7 @@ internal class PurchasesFactory( offeringsManager, createPaywallEventsManager(application, identityManager, eventsDispatcher, backend), paywallPresentedCache, + purchasesStateProvider, ) return Purchases(purchasesOrchestrator) diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesOrchestrator.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesOrchestrator.kt index 5721a9533..bd2f4003f 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesOrchestrator.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesOrchestrator.kt @@ -88,20 +88,16 @@ internal class PurchasesOrchestrator constructor( private val offeringsManager: OfferingsManager, private val paywallEventsManager: PaywallEventsManager?, private val paywallPresentedCache: PaywallPresentedCache, + private val purchasesStateCache: PurchasesStateCache, // This is nullable due to: https://github.com/RevenueCat/purchases-flutter/issues/408 private val mainHandler: Handler? = Handler(Looper.getMainLooper()), ) : LifecycleDelegate, CustomActivityLifecycleHandler { - /** @suppress */ - @Suppress("RedundantGetter", "RedundantSetter") - @Volatile - internal var state = PurchasesState() - @Synchronized - get() = field + internal var state: PurchasesState + get() = purchasesStateCache.purchasesState - @Synchronized set(value) { - field = value + purchasesStateCache.purchasesState = value } var finishTransactions: Boolean diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateCache.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateCache.kt new file mode 100644 index 000000000..dc3989d2e --- /dev/null +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateCache.kt @@ -0,0 +1,7 @@ +package com.revenuecat.purchases + +internal data class PurchasesStateCache( + @get:Synchronized + @set:Synchronized + override var purchasesState: PurchasesState, +) : PurchasesStateProvider diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateProvider.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateProvider.kt new file mode 100644 index 000000000..1dea9aed1 --- /dev/null +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/PurchasesStateProvider.kt @@ -0,0 +1,5 @@ +package com.revenuecat.purchases + +internal interface PurchasesStateProvider { + val purchasesState: PurchasesState +} diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/amazon/AmazonBilling.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/amazon/AmazonBilling.kt index 00e6ab896..93363a391 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/amazon/AmazonBilling.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/amazon/AmazonBilling.kt @@ -16,6 +16,7 @@ import com.revenuecat.purchases.PostReceiptInitiationSource import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCallback import com.revenuecat.purchases.PurchasesErrorCode +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.amazon.handler.ProductDataHandler import com.revenuecat.purchases.amazon.handler.PurchaseHandler import com.revenuecat.purchases.amazon.handler.PurchaseUpdatesHandler @@ -50,13 +51,14 @@ import com.revenuecat.purchases.ProductType as RevenueCatProductType private const val TERM_SKU_JSON_KEY = "termSku" -@SuppressWarnings("LongParameterList") +@SuppressWarnings("LongParameterList", "TooManyFunctions") internal class AmazonBilling constructor( private val applicationContext: Context, private val amazonBackend: AmazonBackend, private val cache: AmazonCache, private val observerMode: Boolean, private val mainHandler: Handler, + stateProvider: PurchasesStateProvider, private val purchasingServiceProvider: PurchasingServiceProvider = DefaultPurchasingServiceProvider(), private val productDataHandler: ProductDataResponseListener = ProductDataHandler(purchasingServiceProvider, mainHandler), @@ -67,7 +69,7 @@ internal class AmazonBilling constructor( ), private val userDataHandler: UserDataResponseListener = UserDataHandler(purchasingServiceProvider, mainHandler), private val dateProvider: DateProvider = DefaultDateProvider(), -) : BillingAbstract(), +) : BillingAbstract(stateProvider), ProductDataResponseListener by productDataHandler, PurchaseResponseListener by purchaseHandler, PurchaseUpdatesResponseListener by purchaseUpdatesHandler, @@ -81,9 +83,17 @@ internal class AmazonBilling constructor( observerMode: Boolean, mainHandler: Handler, backendHelper: BackendHelper, - ) : this(applicationContext, AmazonBackend(backendHelper), AmazonCache(cache), observerMode, mainHandler) - - var connected = false + stateProvider: PurchasesStateProvider, + ) : this( + applicationContext, + AmazonBackend(backendHelper), + AmazonCache(cache), + observerMode, + mainHandler, + stateProvider, + ) + + private var connected = false override fun startConnection() { if (checkObserverMode()) return diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/common/BillingAbstract.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/common/BillingAbstract.kt index 65da09a56..02d1fe3b6 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/common/BillingAbstract.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/common/BillingAbstract.kt @@ -5,6 +5,7 @@ import com.revenuecat.purchases.PostReceiptInitiationSource import com.revenuecat.purchases.ProductType import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCallback +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.models.InAppMessageType import com.revenuecat.purchases.models.PurchasingData import com.revenuecat.purchases.models.StoreProduct @@ -13,7 +14,9 @@ import com.revenuecat.purchases.models.StoreTransaction internal typealias StoreProductsCallback = (List) -> Unit @SuppressWarnings("TooManyFunctions") -internal abstract class BillingAbstract { +internal abstract class BillingAbstract( + protected val purchasesStateProvider: PurchasesStateProvider, +) { @get:Synchronized @set:Synchronized diff --git a/purchases/src/main/kotlin/com/revenuecat/purchases/google/BillingWrapper.kt b/purchases/src/main/kotlin/com/revenuecat/purchases/google/BillingWrapper.kt index f3b7851fe..3546d851a 100644 --- a/purchases/src/main/kotlin/com/revenuecat/purchases/google/BillingWrapper.kt +++ b/purchases/src/main/kotlin/com/revenuecat/purchases/google/BillingWrapper.kt @@ -25,6 +25,7 @@ import com.revenuecat.purchases.ProductType import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCallback import com.revenuecat.purchases.PurchasesErrorCode +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.common.BillingAbstract import com.revenuecat.purchases.common.DateProvider import com.revenuecat.purchases.common.DefaultDateProvider @@ -81,8 +82,9 @@ internal class BillingWrapper( private val deviceCache: DeviceCache, @Suppress("unused") private val diagnosticsTrackerIfEnabled: DiagnosticsTracker?, + purchasesStateProvider: PurchasesStateProvider, private val dateProvider: DateProvider = DefaultDateProvider(), -) : BillingAbstract(), PurchasesUpdatedListener, BillingClientStateListener { +) : BillingAbstract(purchasesStateProvider), PurchasesUpdatedListener, BillingClientStateListener { @get:Synchronized @set:Synchronized @@ -101,6 +103,9 @@ internal class BillingWrapper( @set:Synchronized private var reconnectionAlreadyScheduled = false + val appInBackground: Boolean + get() = purchasesStateProvider.purchasesState.appInBackground + class ClientFactory(private val context: Context) { @UiThread fun buildClient(listener: com.android.billingclient.api.PurchasesUpdatedListener): BillingClient { @@ -523,7 +528,7 @@ internal class BillingWrapper( QueryPurchasesByTypeUseCaseParams( dateProvider, diagnosticsTrackerIfEnabled, - appInBackground = false, + appInBackground = appInBackground, productType = productType, ), onSuccess = { purchases -> diff --git a/purchases/src/test/java/com/revenuecat/purchases/BasePurchasesTest.kt b/purchases/src/test/java/com/revenuecat/purchases/BasePurchasesTest.kt index d4dc00d00..ddb8bf0d9 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/BasePurchasesTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/BasePurchasesTest.kt @@ -70,6 +70,7 @@ internal open class BasePurchasesTest { internal val mockSyncPurchasesHelper = mockk() protected val mockOfferingsManager = mockk() internal val mockPaywallEventsManager = mockk() + private val purchasesStateProvider = PurchasesStateCache(PurchasesState()) protected var capturedPurchasesUpdatedListener = slot() protected var capturedBillingWrapperStateListener = slot() @@ -407,6 +408,7 @@ internal open class BasePurchasesTest { offeringsManager = mockOfferingsManager, paywallEventsManager = mockPaywallEventsManager, paywallPresentedCache = paywallPresentedCache, + purchasesStateCache = purchasesStateProvider, ) purchases = Purchases(purchasesOrchestrator) Purchases.sharedInstance = purchases diff --git a/purchases/src/test/java/com/revenuecat/purchases/BillingFactoryTest.kt b/purchases/src/test/java/com/revenuecat/purchases/BillingFactoryTest.kt index 26f1204f0..237d066eb 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/BillingFactoryTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/BillingFactoryTest.kt @@ -25,7 +25,8 @@ class BillingFactoryTest { mockBackendHelper, mockCache, observerMode = false, - mockDiagnosticsTracker + mockDiagnosticsTracker, + PurchasesStateCache(PurchasesState()) ) } @@ -41,7 +42,8 @@ class BillingFactoryTest { mockBackendHelper, mockCache, observerMode = false, - diagnosticsTrackerIfEnabled = null + diagnosticsTrackerIfEnabled = null, + PurchasesStateCache(PurchasesState()) ) } } diff --git a/purchases/src/test/java/com/revenuecat/purchases/amazon/AmazonBillingTest.kt b/purchases/src/test/java/com/revenuecat/purchases/amazon/AmazonBillingTest.kt index ca0098fc3..01274c92e 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/amazon/AmazonBillingTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/amazon/AmazonBillingTest.kt @@ -11,6 +11,9 @@ import com.revenuecat.purchases.PostReceiptInitiationSource import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCallback import com.revenuecat.purchases.PurchasesErrorCode +import com.revenuecat.purchases.PurchasesState +import com.revenuecat.purchases.PurchasesStateCache +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.amazon.handler.ProductDataHandler import com.revenuecat.purchases.amazon.handler.PurchaseHandler import com.revenuecat.purchases.amazon.handler.PurchaseUpdatesHandler @@ -70,7 +73,8 @@ class AmazonBillingTest { purchaseHandler = mockPurchaseHandler, purchaseUpdatesHandler = mockPurchaseUpdatesHandler, userDataHandler = mockUserDataHandler, - mainHandler = handler + mainHandler = handler, + stateProvider = PurchasesStateCache(PurchasesState()) ) mockSetupFunctions() @@ -90,7 +94,8 @@ class AmazonBillingTest { productDataHandler = mockProductDataHandler, purchaseHandler = mockPurchaseHandler, purchaseUpdatesHandler = mockPurchaseUpdatesHandler, - userDataHandler = mockUserDataHandler + userDataHandler = mockUserDataHandler, + stateProvider = PurchasesStateCache(PurchasesState()) ) mockSetupFunctions() diff --git a/purchases/src/test/java/com/revenuecat/purchases/amazon/BillingFactoryAmazonTest.kt b/purchases/src/test/java/com/revenuecat/purchases/amazon/BillingFactoryAmazonTest.kt index 290397a46..0f3f5fe40 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/amazon/BillingFactoryAmazonTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/amazon/BillingFactoryAmazonTest.kt @@ -3,6 +3,9 @@ package com.revenuecat.purchases.amazon import android.app.Application import androidx.test.ext.junit.runners.AndroidJUnit4 import com.revenuecat.purchases.BillingFactory +import com.revenuecat.purchases.PurchasesState +import com.revenuecat.purchases.PurchasesStateCache +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.Store import com.revenuecat.purchases.common.BackendHelper import com.revenuecat.purchases.common.caching.DeviceCache @@ -27,7 +30,8 @@ class BillingFactoryAmazonTest { mockBackendHelper, mockCache, observerMode = false, - mockDiagnosticsTracker + mockDiagnosticsTracker, + stateProvider = PurchasesStateCache(PurchasesState()) ) } @@ -43,7 +47,8 @@ class BillingFactoryAmazonTest { mockBackendHelper, mockCache, observerMode = false, - diagnosticsTrackerIfEnabled = null + diagnosticsTrackerIfEnabled = null, + stateProvider = PurchasesStateCache(PurchasesState()) ) } } diff --git a/purchases/src/test/java/com/revenuecat/purchases/google/BillingWrapperTest.kt b/purchases/src/test/java/com/revenuecat/purchases/google/BillingWrapperTest.kt index b6f2fa4af..3753bcf48 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/google/BillingWrapperTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/google/BillingWrapperTest.kt @@ -26,6 +26,9 @@ import com.revenuecat.purchases.PostReceiptInitiationSource import com.revenuecat.purchases.ProductType import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCode +import com.revenuecat.purchases.PurchasesState +import com.revenuecat.purchases.PurchasesStateCache +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.assertDebugLog import com.revenuecat.purchases.assertErrorLog import com.revenuecat.purchases.assertVerboseLog @@ -110,6 +113,7 @@ class BillingWrapperTest { private val subsGoogleProductType = ProductType.SUBS.toGoogleProductType()!! private val inAppGoogleProductType = ProductType.INAPP.toGoogleProductType()!! + private val purchasesStateProvider = PurchasesStateCache(PurchasesState()) @Before fun setup() { @@ -153,7 +157,14 @@ class BillingWrapperTest { mockDetailsList = listOf(mockProductDetails()) - wrapper = BillingWrapper(mockClientFactory, handler, mockDeviceCache, mockDiagnosticsTracker, mockDateProvider) + wrapper = BillingWrapper( + mockClientFactory, + handler, + mockDeviceCache, + mockDiagnosticsTracker, + purchasesStateProvider, + mockDateProvider + ) wrapper.purchasesUpdatedListener = mockPurchasesListener wrapper.startConnectionOnMainThread() onConnectedCalled = false diff --git a/purchases/src/test/java/com/revenuecat/purchases/google/usecase/BaseBillingUseCaseTest.kt b/purchases/src/test/java/com/revenuecat/purchases/google/usecase/BaseBillingUseCaseTest.kt index 0b6fecd8f..6c836f9c1 100644 --- a/purchases/src/test/java/com/revenuecat/purchases/google/usecase/BaseBillingUseCaseTest.kt +++ b/purchases/src/test/java/com/revenuecat/purchases/google/usecase/BaseBillingUseCaseTest.kt @@ -5,6 +5,9 @@ import com.android.billingclient.api.BillingClient import com.android.billingclient.api.BillingClientStateListener import com.android.billingclient.api.BillingResult import com.android.billingclient.api.PurchasesUpdatedListener +import com.revenuecat.purchases.PurchasesState +import com.revenuecat.purchases.PurchasesStateCache +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.common.BillingAbstract import com.revenuecat.purchases.common.DateProvider import com.revenuecat.purchases.common.caching.DeviceCache @@ -45,6 +48,7 @@ internal open class BaseBillingUseCaseTest { private var onConnectedCalled: Boolean = false private var mockPurchasesListener: BillingAbstract.PurchasesUpdatedListener = mockk() + private val purchasesStateProvider = PurchasesStateCache(PurchasesState()) @Before open fun setup() { @@ -75,7 +79,14 @@ internal open class BaseBillingUseCaseTest { mockClient.isReady } returns false andThen true - wrapper = BillingWrapper(mockClientFactory, handler, mockDeviceCache, mockDiagnosticsTracker, mockDateProvider) + wrapper = BillingWrapper( + mockClientFactory, + handler, + mockDeviceCache, + mockDiagnosticsTracker, + purchasesStateProvider, + mockDateProvider + ) wrapper.purchasesUpdatedListener = mockPurchasesListener wrapper.startConnectionOnMainThread() onConnectedCalled = false diff --git a/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/attributes/SubscriberAttributesPurchasesTests.kt b/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/attributes/SubscriberAttributesPurchasesTests.kt index bc6fee8dd..a40344dc0 100644 --- a/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/attributes/SubscriberAttributesPurchasesTests.kt +++ b/purchases/src/testDefaults/kotlin/com/revenuecat/purchases/attributes/SubscriberAttributesPurchasesTests.kt @@ -10,6 +10,9 @@ import com.revenuecat.purchases.PostReceiptHelper import com.revenuecat.purchases.PostTransactionWithProductDetailsHelper import com.revenuecat.purchases.Purchases import com.revenuecat.purchases.PurchasesOrchestrator +import com.revenuecat.purchases.PurchasesState +import com.revenuecat.purchases.PurchasesStateCache +import com.revenuecat.purchases.PurchasesStateProvider import com.revenuecat.purchases.Store import com.revenuecat.purchases.common.AppConfig import com.revenuecat.purchases.common.Backend @@ -98,7 +101,8 @@ class SubscriberAttributesPurchasesTests { syncPurchasesHelper = mockk(), offeringsManager = offeringsManagerMock, paywallEventsManager = null, - paywallPresentedCache = PaywallPresentedCache() + paywallPresentedCache = PaywallPresentedCache(), + purchasesStateCache = PurchasesStateCache(PurchasesState()), ) underTest = Purchases(purchasesOrchestrator)