From ffbb40bd43d0e35be8af0241f3650d5db2fcba9e Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Mon, 17 Mar 2025 11:35:13 +0300 Subject: [PATCH 1/3] Added request trigger and attempts index headers to restore and purchase requests. --- .../sdk/internal/OutagerIntegrationTest.kt | 3 ++- .../QonversionRepositoryIntegrationTest.kt | 5 +++-- .../android/sdk/internal/QProductCenterManager.kt | 9 ++++++--- .../android/sdk/internal/QonversionInternal.kt | 5 +++-- .../qonversion/android/sdk/internal/api/Api.kt | 12 ++++++++++-- .../sdk/internal/api/ApiHeadersProvider.kt | 6 +++++- .../sdk/internal/api/NetworkInterceptor.kt | 2 +- .../android/sdk/internal/api/RequestTrigger.kt | 9 +++++++++ .../sdk/internal/repository/DefaultRepository.kt | 15 +++++++++------ .../sdk/internal/repository/QRepository.kt | 4 +++- .../repository/RepositoryWithRateLimits.kt | 8 +++++--- 11 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt diff --git a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt index eccf6956..50089c28 100644 --- a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt +++ b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt @@ -18,6 +18,7 @@ import com.qonversion.android.sdk.dto.offerings.QOffering import com.qonversion.android.sdk.dto.offerings.QOfferingTag import com.qonversion.android.sdk.dto.offerings.QOfferings import com.qonversion.android.sdk.dto.products.QProduct +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.di.QDependencyInjector import com.qonversion.android.sdk.internal.dto.QLaunchResult import com.qonversion.android.sdk.internal.dto.QPermission @@ -269,7 +270,7 @@ internal class OutagerIntegrationTest { fail("Failed to create user") } - repository.restoreRequest(installDate, history, callback) + repository.restoreRequest(installDate, history, callback, RequestTrigger.Restore) } signal.await() diff --git a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt index 89cdee75..e11921a6 100644 --- a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt +++ b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt @@ -20,6 +20,7 @@ import com.qonversion.android.sdk.dto.offerings.QOfferingTag import com.qonversion.android.sdk.dto.offerings.QOfferings import com.qonversion.android.sdk.dto.products.QProduct import com.qonversion.android.sdk.dto.properties.QUserProperty +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.di.QDependencyInjector import com.qonversion.android.sdk.internal.dto.QLaunchResult import com.qonversion.android.sdk.internal.dto.QPermission @@ -339,7 +340,7 @@ internal class QonversionRepositoryIntegrationTest { fail("Failed to create user") } - repository.restoreRequest(installDate, history, callback) + repository.restoreRequest(installDate, history, callback, RequestTrigger.Restore) } signal.await() @@ -373,7 +374,7 @@ internal class QonversionRepositoryIntegrationTest { val repository = initRepository(uid, INCORRECT_PROJECT_KEY) // when - repository.restoreRequest(installDate, history, callback) + repository.restoreRequest(installDate, history, callback, RequestTrigger.Restore) signal.await() } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt index ea46eb23..0947f106 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt @@ -28,6 +28,7 @@ import com.qonversion.android.sdk.dto.entitlements.QEntitlementsCacheLifetime import com.qonversion.android.sdk.dto.offerings.QOfferings import com.qonversion.android.sdk.dto.products.QProduct import com.qonversion.android.sdk.dto.products.QProductType +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.dto.QProductRenewState import com.qonversion.android.sdk.internal.billing.BillingError import com.qonversion.android.sdk.internal.billing.BillingService @@ -394,7 +395,7 @@ internal class QProductCenterManager internal constructor( handlePendingRequests() } - fun restore(callback: QonversionEntitlementsCallback? = null) { + fun restore(requestTrigger: RequestTrigger, callback: QonversionEntitlementsCallback? = null) { callback?.let { restoreCallbacks.add(it) } if (isRestoreInProgress) { @@ -422,12 +423,14 @@ internal class QProductCenterManager internal constructor( executeRestoreBlocksOnError(error) } } - }) + }, + requestTrigger + ) } } fun syncPurchases() { - restore() + restore(RequestTrigger.SyncPurchases) } override fun onPurchasesCompleted(purchases: List) { diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt index 8609181a..96ef0e3b 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt @@ -21,6 +21,7 @@ import com.qonversion.android.sdk.dto.QonversionError import com.qonversion.android.sdk.dto.eligibility.QEligibility import com.qonversion.android.sdk.dto.offerings.QOfferings import com.qonversion.android.sdk.dto.products.QProduct +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.di.QDependencyInjector import com.qonversion.android.sdk.internal.dto.QLaunchResult import com.qonversion.android.sdk.internal.dto.purchase.PurchaseModelInternal @@ -150,7 +151,7 @@ internal class QonversionInternal( return } - Qonversion.shared.restore(callback = object : QonversionEntitlementsCallback { + productCenterManager.restore(RequestTrigger.SyncHistoricalData, callback = object : QonversionEntitlementsCallback { override fun onSuccess(entitlements: Map) { sharedPreferencesCache.putBool(Constants.IS_HISTORICAL_DATA_SYNCED, true) } @@ -342,7 +343,7 @@ internal class QonversionInternal( } override fun restore(callback: QonversionEntitlementsCallback) { - productCenterManager.restore(mainEntitlementsCallback(callback)) + productCenterManager.restore(RequestTrigger.Restore, mainEntitlementsCallback(callback)) } override fun syncPurchases() { diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt index c7d0327a..296b5f74 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt @@ -27,6 +27,7 @@ import retrofit2.http.Body import retrofit2.http.POST import retrofit2.http.GET import retrofit2.http.DELETE +import retrofit2.http.Header import retrofit2.http.Path import retrofit2.http.QueryMap import retrofit2.http.Headers @@ -39,10 +40,17 @@ internal interface Api { fun init(@Body request: InitRequest): Call> @POST("v1/user/purchase") - fun purchase(@Body request: PurchaseRequest): Call> + fun purchase( + @Body request: PurchaseRequest, + @Header("Trigger") trigger: String, + @Header("Attempt") attemptNumber: Int + ): Call> @POST("v1/user/restore") - fun restore(@Body request: RestoreRequest): Call> + fun restore( + @Body request: RestoreRequest, + @Header("Trigger") trigger: String + ): Call> @POST("attribution") fun attribution(@Body request: AttributionRequest): Call> diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/ApiHeadersProvider.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/ApiHeadersProvider.kt index 4dd7b7ac..c9bc42e4 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/ApiHeadersProvider.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/ApiHeadersProvider.kt @@ -22,11 +22,15 @@ internal class ApiHeadersProvider @Inject constructor( private fun getLocale() = Locale.getDefault().language - fun getHeaders(): Headers { + fun getHeaders(specificHeaders: Headers): Headers { val headerBuilder = Headers.Builder() for ((key, value) in getHeadersMap()) { headerBuilder.add(key, value) } + specificHeaders.names().forEach { header -> + val value = specificHeaders.get(header) + value?.let { headerBuilder.add(header, value) } + } return headerBuilder.build() } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/NetworkInterceptor.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/NetworkInterceptor.kt index 40524b9c..2a1b6bce 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/NetworkInterceptor.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/NetworkInterceptor.kt @@ -30,7 +30,7 @@ internal class NetworkInterceptor @Inject constructor( } else { var request = chain.request() request = request.newBuilder() - .headers(headersProvider.getHeaders()) + .headers(headersProvider.getHeaders(request.headers())) .build() val response = chain.proceed(request) diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt new file mode 100644 index 00000000..6694e83d --- /dev/null +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt @@ -0,0 +1,9 @@ +package com.qonversion.android.sdk.internal.api + +// Represents the initial trigger for the API request +internal enum class RequestTrigger(val key: String) { + Purchase("Purchase"), + Restore("Restore"), + SyncHistoricalData("SyncHistoricalData"), + SyncPurchases("SyncPurchases"), +} diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt index 9a200f4c..7a791a11 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt @@ -12,6 +12,7 @@ import com.qonversion.android.sdk.internal.IncrementalDelayCalculator import com.qonversion.android.sdk.internal.InternalConfig import com.qonversion.android.sdk.internal.api.Api import com.qonversion.android.sdk.internal.api.ApiErrorMapper +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.billing.productId import com.qonversion.android.sdk.internal.dto.BaseResponse import com.qonversion.android.sdk.internal.dto.ProviderData @@ -244,11 +245,12 @@ internal class DefaultRepository internal constructor( override fun restore( installDate: Long, historyRecords: List, - callback: QonversionLaunchCallback? + callback: QonversionLaunchCallback, + requestTrigger: RequestTrigger, ) { val history = convertHistory(historyRecords) - restoreRequest(installDate, history, callback) + restoreRequest(installDate, history, callback, requestTrigger) } override fun attribution( @@ -494,7 +496,7 @@ internal class DefaultRepository internal constructor( purchase = convertPurchaseDetails(purchase, qProductId), ) - api.purchase(purchaseRequest).enqueue { + api.purchase(purchaseRequest, RequestTrigger.Purchase.key, attemptIndex + 1).enqueue { onResponse = { logger.debug("purchaseRequest - ${it.getLogMessage()}") val body = it.body() @@ -614,7 +616,8 @@ internal class DefaultRepository internal constructor( internal fun restoreRequest( installDate: Long, history: List, - callback: QonversionLaunchCallback? + callback: QonversionLaunchCallback, + trigger: RequestTrigger, ) { val request = RestoreRequest( installDate = installDate, @@ -626,7 +629,7 @@ internal class DefaultRepository internal constructor( history = history ) - api.restore(request).enqueue { + api.restore(request, trigger.key).enqueue { onResponse = { logger.debug("restoreRequest - ${it.getLogMessage()}") @@ -634,7 +637,7 @@ internal class DefaultRepository internal constructor( } onFailure = { logger.error("restoreRequest - failure - ${it.toQonversionError()}") - callback?.onError(it.toQonversionError()) + callback.onError(it.toQonversionError()) } } } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt index 642715c4..37cfab0d 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt @@ -2,6 +2,7 @@ package com.qonversion.android.sdk.internal.repository import com.qonversion.android.sdk.dto.QonversionError import com.qonversion.android.sdk.dto.properties.QUserProperty +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.dto.SendPropertiesResult import com.qonversion.android.sdk.internal.dto.automations.ActionPointScreen import com.qonversion.android.sdk.internal.dto.automations.Screen @@ -61,7 +62,8 @@ internal interface QRepository { fun restore( installDate: Long, historyRecords: List, - callback: QonversionLaunchCallback? + callback: QonversionLaunchCallback, + requestTrigger: RequestTrigger, ) fun attribution( diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt index 70f99e50..aa692398 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt @@ -5,6 +5,7 @@ import com.qonversion.android.sdk.dto.QonversionErrorCode import com.qonversion.android.sdk.dto.properties.QUserProperty import com.qonversion.android.sdk.internal.api.RequestType import com.qonversion.android.sdk.internal.api.RateLimiter +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.dto.SendPropertiesResult import com.qonversion.android.sdk.internal.dto.automations.ActionPointScreen import com.qonversion.android.sdk.internal.dto.automations.Screen @@ -138,14 +139,15 @@ internal class RepositoryWithRateLimits( override fun restore( installDate: Long, historyRecords: List, - callback: QonversionLaunchCallback? + callback: QonversionLaunchCallback, + requestTrigger: RequestTrigger, ) { withRateLimitCheck( RequestType.Restore, installDate.hashCode() + historyRecords.hashCode(), - { error -> callback?.onError(error) } + { error -> callback.onError(error) } ) { - repository.restore(installDate, historyRecords, callback) + repository.restore(installDate, historyRecords, callback, requestTrigger) } } From 5b0f861dc460cc547bf7632c5aa9b8dc147c6947 Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Mon, 17 Mar 2025 11:36:37 +0300 Subject: [PATCH 2/3] Detekt fixes --- config/detekt/baseline.xml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 52353500..7e27a05c 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -122,6 +122,7 @@ MaxLineLength:QonversionError.kt$QonversionErrorCode$RemoteConfigurationNotAvailable : QonversionErrorCode MaxLineLength:QonversionInternal.kt$QonversionInternal$@Deprecated("Use the new purchase() method", replaceWith = ReplaceWith("purchase(context, TODO(\"pass product here\"), callback)")) MaxLineLength:QonversionInternal.kt$QonversionInternal$@Deprecated("Use the new updatePurchase() method", replaceWith = ReplaceWith("updatePurchase(context, TODO(\"pass product here\"), TODO(\"pass purchase options here\"), callback)")) + MaxLineLength:QonversionInternal.kt$QonversionInternal$productCenterManager MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"""HTTP status code=400, data={"message":"Invalid access token received","code":10003,"status":400,"extra":[]}. """ MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"efnkoceomiocidgigbalneag.AO-J1Ow8JyXX8gs8W9blILbU9Nqy3Mr-RMTLgFG2DLOhO1urkWpr80PUSE6eA0IuEzl_k4Guecep5JZJwcnSYOWzTZhqwusafayRCbv4kRMUR-rR9Ot11nQ" MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"lgeigljfpmeoddkcebkcepjc.AO-J1Oy305qZj99jXTPEVBN8UZGoYAtjDLj4uTjRQvUFaG0vie-nr6VBlN0qnNDMU8eJR-sI7o3CwQyMOEHKl8eJsoQ86KSFzxKBR07PSpHLI_o7agXhNKY" @@ -157,20 +158,21 @@ MaximumLineLength:com.qonversion.android.sdk.dto.products.QProduct.kt:120 MaximumLineLength:com.qonversion.android.sdk.dto.products.QProduct.kt:139 MaximumLineLength:com.qonversion.android.sdk.dto.products.QProductStoreDetails.kt:130 - MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:214 - MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:371 - MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:90 - MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManager.kt:333 + MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:215 + MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:372 + MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:91 + MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManager.kt:334 MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManagerTest.kt:152 MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManagerTest.kt:153 MaximumLineLength:com.qonversion.android.sdk.internal.QRemoteConfigManager.kt:225 MaximumLineLength:com.qonversion.android.sdk.internal.QUserPropertiesManagerTest.kt:178 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:164 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:215 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:287 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:356 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:878 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:89 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:154 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:165 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:216 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:288 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:357 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:879 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:90 MaximumLineLength:com.qonversion.android.sdk.internal.api.ApiErrorMapper.kt:118 MaximumLineLength:com.qonversion.android.sdk.internal.api.ApiErrorMapper.kt:119 MaximumLineLength:com.qonversion.android.sdk.internal.billing.QonversionBillingService.kt:253 From d032204e0d30ac357df6f09ed2db18dcdd79f6ce Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Tue, 18 Mar 2025 13:31:31 +0300 Subject: [PATCH 3/3] Added init request triggers sending. --- config/detekt/baseline.xml | 19 +++--- .../sdk/internal/OutagerIntegrationTest.kt | 6 +- .../QonversionRepositoryIntegrationTest.kt | 9 ++- .../sdk/internal/QProductCenterManager.kt | 48 ++++++++----- .../sdk/internal/QUserPropertiesManager.kt | 20 +++--- .../sdk/internal/QonversionInternal.kt | 12 +--- .../android/sdk/internal/api/Api.kt | 5 +- .../sdk/internal/api/RequestTrigger.kt | 6 ++ .../dto/request/data/InitRequestData.kt | 4 +- .../internal/repository/DefaultRepository.kt | 67 +++++++++---------- .../sdk/internal/repository/QRepository.kt | 2 +- .../repository/RepositoryWithRateLimits.kt | 8 +-- 12 files changed, 110 insertions(+), 96 deletions(-) diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 7e27a05c..09b04a62 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -12,7 +12,6 @@ EmptyFunctionBlock:QAutomationsManagerTest.kt$QAutomationsManagerTest.<no name provided>${} EmptyFunctionBlock:QIdentityManagerTest.kt$QIdentityManagerTest.Identify.<no name provided>${} EmptyFunctionBlock:QProductCenterManager.kt$QProductCenterManager.<no name provided>${} - EmptyFunctionBlock:QonversionInternal.kt$QonversionInternal.<no name provided>${} Filename:com.qonversion.android.sdk.internal.storage.util.kt:1 FinalNewline:com.qonversion.android.sdk.automations.internal.AutomationsEventMapperTest.kt:1 FinalNewline:com.qonversion.android.sdk.automations.internal.QAutomationsManagerTest.kt:1 @@ -158,20 +157,20 @@ MaximumLineLength:com.qonversion.android.sdk.dto.products.QProduct.kt:120 MaximumLineLength:com.qonversion.android.sdk.dto.products.QProduct.kt:139 MaximumLineLength:com.qonversion.android.sdk.dto.products.QProductStoreDetails.kt:130 - MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:215 - MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:372 + MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:216 + MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:373 MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:91 - MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManager.kt:334 + MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManager.kt:341 MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManagerTest.kt:152 MaximumLineLength:com.qonversion.android.sdk.internal.QProductCenterManagerTest.kt:153 MaximumLineLength:com.qonversion.android.sdk.internal.QRemoteConfigManager.kt:225 MaximumLineLength:com.qonversion.android.sdk.internal.QUserPropertiesManagerTest.kt:178 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:154 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:165 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:216 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:288 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:357 - MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:879 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:144 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:155 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionInternal.kt:206 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:290 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:359 + MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:882 MaximumLineLength:com.qonversion.android.sdk.internal.QonversionRepositoryIntegrationTest.kt:90 MaximumLineLength:com.qonversion.android.sdk.internal.api.ApiErrorMapper.kt:118 MaximumLineLength:com.qonversion.android.sdk.internal.api.ApiErrorMapper.kt:119 diff --git a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt index 50089c28..b4f5b4a2 100644 --- a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt +++ b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/OutagerIntegrationTest.kt @@ -125,7 +125,8 @@ internal class OutagerIntegrationTest { override fun onError(error: QonversionError) { fail("Shouldn't fail") } - } + }, + RequestTrigger.Init ) val repository = initRepository(uid) @@ -526,7 +527,8 @@ internal class OutagerIntegrationTest { override fun onError(error: QonversionError) { onComplete(error) } - } + }, + RequestTrigger.Init ) repository.init(data) } diff --git a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt index e11921a6..46c1bfb0 100644 --- a/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt +++ b/sdk/src/androidTest/java/com/qonversion/android/sdk/internal/QonversionRepositoryIntegrationTest.kt @@ -120,7 +120,8 @@ internal class QonversionRepositoryIntegrationTest { override fun onError(error: QonversionError) { fail("Shouldn't fail") } - } + }, + RequestTrigger.Init ) val repository = initRepository(uid) @@ -151,7 +152,8 @@ internal class QonversionRepositoryIntegrationTest { assertIncorrectProjectKeyError(error) signal.countDown() } - } + }, + RequestTrigger.Init ) val repository = initRepository(uid, INCORRECT_PROJECT_KEY) @@ -851,7 +853,8 @@ internal class QonversionRepositoryIntegrationTest { override fun onError(error: QonversionError) { onComplete(error) } - } + }, + RequestTrigger.Init ) repository.init(data) } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt index 0947f106..0f1490de 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QProductCenterManager.kt @@ -127,7 +127,7 @@ internal class QProductCenterManager internal constructor( processPendingInitIfAvailable() } - fun launch(callback: QonversionLaunchCallback? = null) { + fun launch(requestTrigger: RequestTrigger, callback: QonversionLaunchCallback? = null) { val launchCallback: QonversionLaunchCallback = getLaunchCallback(callback) launchError = null launchResultCache.resetSessionCache() @@ -137,15 +137,15 @@ internal class QProductCenterManager internal constructor( adProvider.init(context, object : AdvertisingProvider.Callback { override fun onSuccess(advertisingId: String) { advertisingID = advertisingId - continueLaunchWithPurchasesInfo(launchCallback) + continueLaunchWithPurchasesInfo(launchCallback, requestTrigger) } override fun onFailure(t: Throwable) { - continueLaunchWithPurchasesInfo(launchCallback) + continueLaunchWithPurchasesInfo(launchCallback, requestTrigger) } }) } else { - continueLaunchWithPurchasesInfo(launchCallback) + continueLaunchWithPurchasesInfo(launchCallback, requestTrigger) } } @@ -157,7 +157,7 @@ internal class QProductCenterManager internal constructor( launchResultCache.sessionLaunchResult?.let { loadStoreProductsIfPossible() - } ?: launch() + } ?: launch(RequestTrigger.Products) } fun offerings( @@ -205,7 +205,13 @@ internal class QProductCenterManager internal constructor( } } - val initRequestData = InitRequestData(installDate, advertisingID, callback = launchCallback) + val initRequestData = InitRequestData( + installDate, + advertisingID, + null, + launchCallback, + RequestTrigger.Identify + ) repository.init(initRequestData) } else { processIdentity(identityId) @@ -227,7 +233,7 @@ internal class QProductCenterManager internal constructor( internalConfig.uid = qonversionUid remoteConfigManager.onUserUpdate() launchResultCache.clearPermissionsCache() - launch(object : QonversionLaunchCallback { + launch(RequestTrigger.Identify, object : QonversionLaunchCallback { override fun onSuccess(launchResult: QLaunchResult) { fireIdentitySuccess(identityId) } @@ -310,7 +316,8 @@ internal class QProductCenterManager internal constructor( if (launchError != null) { retryLaunch( onSuccess = { tryToPurchase() }, - onError = { tryToPurchase() } + onError = { tryToPurchase() }, + requestTrigger = RequestTrigger.Purchase, ) } else { tryToPurchase() @@ -660,11 +667,12 @@ internal class QProductCenterManager internal constructor( } private fun continueLaunchWithPurchasesInfo( - callback: QonversionLaunchCallback? + callback: QonversionLaunchCallback, + requestTrigger: RequestTrigger, ) { fun processInitDefault() { val initRequestData = - InitRequestData(installDate, advertisingID, callback = callback) + InitRequestData(installDate, advertisingID, null, callback, requestTrigger) processInit(initRequestData) } @@ -690,7 +698,8 @@ internal class QProductCenterManager internal constructor( installDate, advertisingID, purchasesInfo, - handledPurchasesCallback + handledPurchasesCallback, + requestTrigger ) processInit(initRequestData) } @@ -698,7 +707,7 @@ internal class QProductCenterManager internal constructor( private fun getWrappedPurchasesCallback( trackingPurchases: List, - outerCallback: QonversionLaunchCallback? + outerCallback: QonversionLaunchCallback ): QonversionLaunchCallback { return object : QonversionLaunchCallback { override fun onSuccess(launchResult: QLaunchResult) { @@ -706,11 +715,11 @@ internal class QProductCenterManager internal constructor( trackingPurchases.forEach { removePurchaseOptions(it.productId) } - outerCallback?.onSuccess(launchResult) + outerCallback.onSuccess(launchResult) } override fun onError(error: QonversionError) { - outerCallback?.onError(error) + outerCallback.onError(error) } } } @@ -776,7 +785,7 @@ internal class QProductCenterManager internal constructor( private fun handleLogout() { unhandledLogoutAvailable = false - launch() + launch(RequestTrigger.Logout) } private fun updateLaunchResult(launchResult: QLaunchResult) { @@ -921,9 +930,10 @@ internal class QProductCenterManager internal constructor( private fun retryLaunch( onSuccess: (QLaunchResult) -> Unit, - onError: (QonversionError) -> Unit + onError: (QonversionError) -> Unit, + requestTrigger: RequestTrigger, ) { - launch(object : QonversionLaunchCallback { + launch(requestTrigger, object : QonversionLaunchCallback { override fun onSuccess(launchResult: QLaunchResult) = onSuccess(launchResult) override fun onError(error: QonversionError) = onError(error) }) @@ -957,7 +967,9 @@ internal class QProductCenterManager internal constructor( cachedPermissions?.let { onSuccess(it) } ?: onError(error) - }) + }, + requestTrigger = RequestTrigger.ActualizePermissions + ) } val permissions = launchResultCache.getActualPermissions() ?: emptyMap() diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QUserPropertiesManager.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QUserPropertiesManager.kt index 5dbd1980..23ad324d 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QUserPropertiesManager.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QUserPropertiesManager.kt @@ -8,6 +8,7 @@ import com.qonversion.android.sdk.dto.properties.QUserPropertyKey import com.qonversion.android.sdk.dto.QonversionError import com.qonversion.android.sdk.dto.QonversionErrorCode import com.qonversion.android.sdk.dto.properties.QUserProperties +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.listeners.QonversionLaunchCallback import com.qonversion.android.sdk.internal.dto.QLaunchResult import com.qonversion.android.sdk.internal.logger.Logger @@ -106,15 +107,18 @@ internal class QUserPropertiesManager @Inject internal constructor( isRequestInProgress = false if (it.code === QonversionErrorCode.InvalidClientUid) { - productCenterManager?.launch(callback = object : QonversionLaunchCallback { - override fun onSuccess(launchResult: QLaunchResult) { - retryPropertiesRequest() + productCenterManager?.launch( + RequestTrigger.UserProperties, + object : QonversionLaunchCallback { + override fun onSuccess(launchResult: QLaunchResult) { + retryPropertiesRequest() + } + + override fun onError(error: QonversionError) { + retryPropertiesRequest() + } } - - override fun onError(error: QonversionError) { - retryPropertiesRequest() - } - }) + ) } else { retryPropertiesRequest() } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt index 96ef0e3b..c2080d5c 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QonversionInternal.kt @@ -23,7 +23,6 @@ import com.qonversion.android.sdk.dto.offerings.QOfferings import com.qonversion.android.sdk.dto.products.QProduct import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.internal.di.QDependencyInjector -import com.qonversion.android.sdk.internal.dto.QLaunchResult import com.qonversion.android.sdk.internal.dto.purchase.PurchaseModelInternal import com.qonversion.android.sdk.internal.logger.ConsoleLogger import com.qonversion.android.sdk.internal.logger.ExceptionManager @@ -32,7 +31,6 @@ import com.qonversion.android.sdk.internal.services.QFallbacksService import com.qonversion.android.sdk.internal.storage.SharedPreferencesCache import com.qonversion.android.sdk.listeners.QonversionExperimentAttachCallback import com.qonversion.android.sdk.listeners.QonversionEntitlementsCallback -import com.qonversion.android.sdk.listeners.QonversionLaunchCallback import com.qonversion.android.sdk.listeners.QonversionOfferingsCallback import com.qonversion.android.sdk.listeners.QonversionProductsCallback import com.qonversion.android.sdk.listeners.QonversionRemoteConfigCallback @@ -119,7 +117,7 @@ internal class QonversionInternal( val lifecycleHandler = AppLifecycleHandler(this) postToMainThread { ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleHandler) } - launch() + productCenterManager.launch(RequestTrigger.Init) } override fun onAppBackground() { @@ -136,14 +134,6 @@ internal class QonversionInternal( attributionManager.onAppForeground() } - private fun launch() { - productCenterManager.launch(object : QonversionLaunchCallback { - override fun onSuccess(launchResult: QLaunchResult) {} - - override fun onError(error: QonversionError) {} - }) - } - override fun syncHistoricalData() { val isHistoricalDataSynced: Boolean = sharedPreferencesCache.getBool(Constants.IS_HISTORICAL_DATA_SYNCED) diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt index 296b5f74..ad5860ec 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/Api.kt @@ -37,7 +37,10 @@ import retrofit2.http.Url internal interface Api { @POST("v1/user/init") - fun init(@Body request: InitRequest): Call> + fun init( + @Body request: InitRequest, + @Header("Trigger") trigger: String + ): Call> @POST("v1/user/purchase") fun purchase( diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt index 6694e83d..ee31c818 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/api/RequestTrigger.kt @@ -2,8 +2,14 @@ package com.qonversion.android.sdk.internal.api // Represents the initial trigger for the API request internal enum class RequestTrigger(val key: String) { + Init("Init"), + Identify("Identify"), + Products("Products"), Purchase("Purchase"), + UserProperties("UserProperties"), Restore("Restore"), SyncHistoricalData("SyncHistoricalData"), SyncPurchases("SyncPurchases"), + ActualizePermissions("ActualizePermissions"), + Logout("Logout"), } diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/dto/request/data/InitRequestData.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/dto/request/data/InitRequestData.kt index 4946e25d..863f8435 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/dto/request/data/InitRequestData.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/dto/request/data/InitRequestData.kt @@ -1,5 +1,6 @@ package com.qonversion.android.sdk.internal.dto.request.data +import com.qonversion.android.sdk.internal.api.RequestTrigger import com.qonversion.android.sdk.listeners.QonversionLaunchCallback import com.qonversion.android.sdk.internal.purchase.Purchase @@ -7,5 +8,6 @@ internal data class InitRequestData( val installDate: Long, val idfa: String? = null, val purchases: List? = null, - val callback: QonversionLaunchCallback? = null + val callback: QonversionLaunchCallback? = null, + val requestTrigger: RequestTrigger, ) diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt index 7a791a11..46f25115 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/DefaultRepository.kt @@ -73,11 +73,37 @@ internal class DefaultRepository internal constructor( // Public functions - override fun init(initRequestData: InitRequestData) { - advertisingId = initRequestData.idfa - this.installDate = initRequestData.installDate + override fun init(requestData: InitRequestData) { + advertisingId = requestData.idfa + this.installDate = requestData.installDate - initRequest(initRequestData.purchases, initRequestData.callback) + val inapps: List = convertPurchases(requestData.purchases) + val initRequest = InitRequest( + installDate = installDate, + device = environmentProvider.getInfo(advertisingId), + version = sdkVersion, + accessToken = key, + clientUid = uid, + debugMode = isDebugMode.stringValue(), + purchases = inapps + ) + + api.init(initRequest, requestData.requestTrigger.key).enqueue { + onResponse = { + logger.debug("initRequest - ${it.getLogMessage()}") + + val body = it.body() + if (body != null && body.success) { + requestData.callback?.onSuccess(body.data) + } else { + requestData.callback?.onError(errorMapper.getErrorFromResponse(it)) + } + } + onFailure = { + logger.error("initRequest - failure - ${it.toQonversionError()}") + requestData.callback?.onError(it.toQonversionError()) + } + } } override fun remoteConfig(contextKey: String?, callback: QonversionRemoteConfigCallback) { @@ -654,39 +680,6 @@ internal class DefaultRepository internal constructor( } } - private fun initRequest( - purchases: List? = null, - callback: QonversionLaunchCallback? = null - ) { - val inapps: List = convertPurchases(purchases) - val initRequest = InitRequest( - installDate = installDate, - device = environmentProvider.getInfo(advertisingId), - version = sdkVersion, - accessToken = key, - clientUid = uid, - debugMode = isDebugMode.stringValue(), - purchases = inapps - ) - - api.init(initRequest).enqueue { - onResponse = { - logger.debug("initRequest - ${it.getLogMessage()}") - - val body = it.body() - if (body != null && body.success) { - callback?.onSuccess(body.data) - } else { - callback?.onError(errorMapper.getErrorFromResponse(it)) - } - } - onFailure = { - logger.error("initRequest - failure - ${it.toQonversionError()}") - callback?.onError(it.toQonversionError()) - } - } - } - private fun Response.getLogMessage() = if (isSuccessful) "success - $this" else "failure - ${errorMapper.getErrorFromResponse(this)}" diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt index 37cfab0d..b346bfc7 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/QRepository.kt @@ -19,7 +19,7 @@ import com.qonversion.android.sdk.listeners.QonversionRemoteConfigurationAttachC internal interface QRepository { - fun init(initRequestData: InitRequestData) + fun init(requestData: InitRequestData) fun remoteConfig(contextKey: String?, callback: QonversionRemoteConfigCallback) diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt index aa692398..491ba665 100644 --- a/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt +++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/repository/RepositoryWithRateLimits.kt @@ -24,13 +24,13 @@ internal class RepositoryWithRateLimits( private val repository: QRepository, private val rateLimiter: RateLimiter, ) : QRepository { - override fun init(initRequestData: InitRequestData) { + override fun init(requestData: InitRequestData) { withRateLimitCheck( RequestType.Init, - initRequestData.hashCode(), - { error -> initRequestData.callback?.onError(error) } + requestData.hashCode(), + { error -> requestData.callback?.onError(error) } ) { - repository.init(initRequestData) + repository.init(requestData) } }