diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index 466ca274..b8f6af31 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -6,7 +6,6 @@
ComplexMethod:ScreenPresenter.kt$ScreenPresenter$override fun shouldOverrideUrlLoading(url: String?): Boolean
ComplexMethod:errors.kt$internal fun BillingError.toQonversionError(): QonversionError
ConstructorParameterNaming:Environment.kt$Environment$@Json(name = "app_version") val app_version: String
- ConstructorParameterNaming:QRemoteConfig.kt$QRemoteConfig$@Json(name = "source") internal val _source: QRemoteConfigurationSource?
EmptyCatchBlock:AdvertisingProvider.kt$AdvertisingProvider.AdvertisingConnection${ }
EmptyFunctionBlock:AdvertisingProvider.kt$AdvertisingProvider.AdvertisingConnection${}
EmptyFunctionBlock:QAutomationsManagerTest.kt$QAutomationsManagerTest.<no name provided>${}
@@ -113,6 +112,7 @@
MaxLineLength:QonversionBillingService.kt$QonversionBillingService${ error -> logger.release("Failed to fetch product type for purchase $productId - " + error.message) }
MaxLineLength:QonversionConfig.kt$QonversionConfig.Builder$*
MaxLineLength:QonversionError.kt$QonversionErrorCode$*
+ MaxLineLength:QonversionError.kt$QonversionErrorCode$RemoteConfigurationNotAvailable : QonversionErrorCode
MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"""HTTP status code=400, data={"message":"Invalid access token received","code":10003,"status":400,"extra":[]}. """
MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"lcbfeigohklhpdgmpildjabg.AO-J1OyV-EE2bKGqDcRCvqjZ2NI1uHDRuvonRn5RorP6LNsyK7yHK8FaFlXp6bjTEX3-4JvZKtbY_bpquKBfux09Mfkx05M9YGZsfsr5BJk74r719m77Oyo"
MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"lgeigljfpmeoddkcebkcepjc.AO-J1Oy305qZj99jXTPEVBN8UZGoYAtjDLj4uTjRQvUFaG0vie-nr6VBlN0qnNDMU8eJR-sI7o3CwQyMOEHKl8eJsoQ86KSFzxKBR07PSpHLI_o7agXhNKY"
@@ -140,6 +140,7 @@
MaximumLineLength:com.qonversion.android.sdk.automations.internal.QAutomationsManager.kt:135
MaximumLineLength:com.qonversion.android.sdk.automations.internal.QAutomationsManager.kt:142
MaximumLineLength:com.qonversion.android.sdk.automations.mvp.ScreenPresenterTest.kt:159
+ MaximumLineLength:com.qonversion.android.sdk.dto.QonversionError.kt:45
MaximumLineLength:com.qonversion.android.sdk.dto.products.QProductStoreDetails.kt:130
MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:213
MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:370
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt b/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt
index 2ff93d21..3fce05a9 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt
@@ -116,12 +116,19 @@ interface Qonversion {
fun offerings(callback: QonversionOfferingsCallback)
/**
- * Returns Qonversion remote config object
+ * Returns default Qonversion remote config object
* Use this function to get the remote config with specific payload and experiment info.
* @param callback - callback that will be called when response is received
*/
fun remoteConfig(callback: QonversionRemoteConfigCallback)
+ /**
+ * Returns Qonversion remote config object by [contextKey].
+ * Use this function to get the remote config with specific payload and experiment info.
+ * @param callback - callback that will be called when response is received
+ */
+ fun remoteConfig(contextKey: String, callback: QonversionRemoteConfigCallback)
+
/**
* This function should be used for the test purposes only. Do not forget to delete the usage of this function before the release.
* Use this function to attach the user to the experiment.
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfig.kt b/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfig.kt
index 457d64e6..d6d4257b 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfig.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfig.kt
@@ -8,7 +8,7 @@ import com.squareup.moshi.JsonClass
data class QRemoteConfig internal constructor(
@Json(name = "payload") val payload: Map,
@Json(name = "experiment") val experiment: QExperiment?,
- @Json(name = "source") internal val _source: QRemoteConfigurationSource?
+ @Json(name = "source") internal val sourceApi: QRemoteConfigurationSource?
) {
- val source: QRemoteConfigurationSource get() = _source!!
+ val source: QRemoteConfigurationSource get() = sourceApi!!
}
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfigurationSource.kt b/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfigurationSource.kt
index 1badb515..c52d4c67 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfigurationSource.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/dto/QRemoteConfigurationSource.kt
@@ -9,4 +9,7 @@ data class QRemoteConfigurationSource(
@Json(name = "name") val name: String,
@Json(name = "assignment_type") val assignmentType: QRemoteConfigurationAssignmentType,
@Json(name = "type") val type: QRemoteConfigurationSourceType,
-)
+ @Json(name = "context_key") internal val contextKeyApi: String?
+) {
+ val contextKey: String? = contextKeyApi?.takeIf { it.isNotEmpty() }
+}
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/dto/QonversionError.kt b/sdk/src/main/java/com/qonversion/android/sdk/dto/QonversionError.kt
index b9639e6e..0a41eca1 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/dto/QonversionError.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/dto/QonversionError.kt
@@ -42,6 +42,6 @@ enum class QonversionErrorCode(val specification: String) {
FraudPurchase("Fraud purchase was detected"),
ProjectConfigError("The project is not configured or configured incorrectly in the Qonversion Dashboard"),
InvalidStoreCredentials("This account does not have access to the requested application"),
- RemoteConfigurationNotAvailable("Remote configuration is not available for the current user"),
+ RemoteConfigurationNotAvailable("Remote configuration is not available for the current user or for the provided context key"),
ApiRateLimitExceeded("API requests rate limit exceeded"),
}
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/QRemoteConfigManager.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/QRemoteConfigManager.kt
index b15616f7..4ea01f0d 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/internal/QRemoteConfigManager.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/QRemoteConfigManager.kt
@@ -13,62 +13,71 @@ internal class QRemoteConfigManager @Inject constructor(
private val remoteConfigService: QRemoteConfigService,
private val internalConfig: InternalConfig
) {
+ internal class LoadingState(
+ var loadedConfig: QRemoteConfig? = null,
+ val callbacks: MutableList = mutableListOf(),
+ var isInProgress: Boolean = false
+ )
+
lateinit var userStateProvider: UserStateProvider
- private var currentRemoteConfig: QRemoteConfig? = null
- private var remoteConfigCallbacks = mutableListOf()
- private var isRequestInProgress: Boolean = false
+ private var loadingStates = mutableMapOf()
fun handlePendingRequests() {
- if (remoteConfigCallbacks.isNotEmpty()) {
- loadRemoteConfig(null)
- }
+ loadingStates.filter { it.value.callbacks.isNotEmpty() }
+ .keys.forEach { contextKey -> loadRemoteConfig(contextKey, null) }
}
fun userChangingRequestFailedWithError(error: QonversionError) {
- fireToCallbacks { onError(error) }
+ loadingStates.keys.forEach {
+ fireToCallbacks(it) { onError(error) }
+ }
}
fun onUserUpdate() {
- currentRemoteConfig = null
+ loadingStates = mutableMapOf()
}
- fun loadRemoteConfig(callback: QonversionRemoteConfigCallback?) {
- currentRemoteConfig?.takeIf { userStateProvider.isUserStable }?.let {
- callback?.onSuccess(it)
- return
- }
+ fun loadRemoteConfig(contextKey: String?, callback: QonversionRemoteConfigCallback?) {
+ loadingStates[contextKey]
+ ?.loadedConfig
+ ?.takeIf { userStateProvider.isUserStable }
+ ?.let {
+ callback?.onSuccess(it)
+ return
+ }
+
+ val loadingState = loadingStates[contextKey] ?: LoadingState()
+ loadingStates[contextKey] = loadingState
callback?.let {
- remoteConfigCallbacks.add(it)
+ loadingState.callbacks.add(it)
}
- if (!userStateProvider.isUserStable || isRequestInProgress) {
+ if (!userStateProvider.isUserStable || loadingState.isInProgress) {
return
}
- isRequestInProgress = true
- currentRemoteConfig = null
- remoteConfigService.loadRemoteConfig(internalConfig.uid, object : QonversionRemoteConfigCallback {
+ loadingState.isInProgress = true
+ loadingState.loadedConfig = null
+ remoteConfigService.loadRemoteConfig(internalConfig.uid, contextKey, object : QonversionRemoteConfigCallback {
override fun onSuccess(remoteConfig: QRemoteConfig) {
- isRequestInProgress = false
- currentRemoteConfig = remoteConfig
- fireToCallbacks { onSuccess(remoteConfig) }
+ loadingState.loadedConfig = remoteConfig
+ fireToCallbacks(contextKey) { onSuccess(remoteConfig) }
}
override fun onError(error: QonversionError) {
- isRequestInProgress = false
- fireToCallbacks { onError(error) }
+ fireToCallbacks(contextKey) { onError(error) }
}
})
}
fun attachUserToExperiment(experimentId: String, groupId: String, callback: QonversionExperimentAttachCallback) {
- currentRemoteConfig = null
+ loadingStates[null]?.loadedConfig = null
remoteConfigService.attachUserToExperiment(experimentId, groupId, internalConfig.uid, callback)
}
fun detachUserFromExperiment(experimentId: String, callback: QonversionExperimentAttachCallback) {
- currentRemoteConfig = null
+ loadingStates[null]?.loadedConfig = null
remoteConfigService.detachUserFromExperiment(experimentId, internalConfig.uid, callback)
}
@@ -76,7 +85,7 @@ internal class QRemoteConfigManager @Inject constructor(
remoteConfigurationId: String,
callback: QonversionRemoteConfigurationAttachCallback
) {
- currentRemoteConfig = null
+ loadingStates[null]?.loadedConfig = null
remoteConfigService.attachUserToRemoteConfiguration(remoteConfigurationId, internalConfig.uid, callback)
}
@@ -84,13 +93,16 @@ internal class QRemoteConfigManager @Inject constructor(
remoteConfigurationId: String,
callback: QonversionRemoteConfigurationAttachCallback
) {
- currentRemoteConfig = null
+ loadingStates[null]?.loadedConfig = null
remoteConfigService.detachUserFromRemoteConfiguration(remoteConfigurationId, internalConfig.uid, callback)
}
- private fun fireToCallbacks(action: QonversionRemoteConfigCallback.() -> Unit) {
- val callbacks = remoteConfigCallbacks.toList()
- callbacks.forEach { it.action() }
- remoteConfigCallbacks.clear()
+ private fun fireToCallbacks(contextKey: String?, action: QonversionRemoteConfigCallback.() -> Unit) {
+ loadingStates[contextKey]?.let { loadingState ->
+ loadingState.isInProgress = false
+ val callbacks = loadingState.callbacks.toList()
+ loadingState.callbacks.clear()
+ callbacks.forEach { it.action() }
+ }
}
}
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 f6574166..13b81d8e 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
@@ -194,7 +194,15 @@ internal class QonversionInternal(
}
override fun remoteConfig(callback: QonversionRemoteConfigCallback) {
- remoteConfigManager?.loadRemoteConfig(object : QonversionRemoteConfigCallback {
+ loadRemoteConfig(null, callback)
+ }
+
+ override fun remoteConfig(contextKey: String, callback: QonversionRemoteConfigCallback) {
+ loadRemoteConfig(contextKey, callback)
+ }
+
+ private fun loadRemoteConfig(contextKey: String?, callback: QonversionRemoteConfigCallback) {
+ remoteConfigManager?.loadRemoteConfig(contextKey, object : QonversionRemoteConfigCallback {
override fun onSuccess(remoteConfig: QRemoteConfig) {
postToMainThread { callback.onSuccess(remoteConfig) }
}
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 72f513db..cb330e30 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
@@ -82,8 +82,11 @@ internal class DefaultRepository internal constructor(
initRequest(initRequestData.purchases, initRequestData.callback)
}
- override fun remoteConfig(userID: String, callback: QonversionRemoteConfigCallback) {
- val queryParams = mapOf("user_id" to userID)
+ override fun remoteConfig(userID: String, contextKey: String?, callback: QonversionRemoteConfigCallback) {
+ val queryParams = mapOf("user_id" to userID, "context_key" to contextKey)
+ .filterValues { it != null }
+ .mapValues { it.value!! }
+
api.remoteConfig(queryParams).enqueue {
onResponse = {
logger.debug("remoteConfigRequest - ${it.getLogMessage()}")
@@ -91,7 +94,7 @@ internal class DefaultRepository internal constructor(
if (body == null) {
callback.onError(errorMapper.getErrorFromResponse(it))
} else {
- if (body.payload.isEmpty() && body._source == null) {
+ if (body.payload.isEmpty() && body.sourceApi == null) {
callback.onError(QonversionError(QonversionErrorCode.RemoteConfigurationNotAvailable))
} else {
callback.onSuccess(body)
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 55f2c0ac..3952957f 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 @@ internal interface QRepository {
fun init(initRequestData: InitRequestData)
- fun remoteConfig(userID: String, callback: QonversionRemoteConfigCallback)
+ fun remoteConfig(userID: String, contextKey: String?, callback: QonversionRemoteConfigCallback)
fun attachUserToExperiment(
experimentId: String,
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 a1e608b2..bdbd08c9 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
@@ -32,13 +32,13 @@ internal class RepositoryWithRateLimits(
}
}
- override fun remoteConfig(userID: String, callback: QonversionRemoteConfigCallback) {
+ override fun remoteConfig(userID: String, contextKey: String?, callback: QonversionRemoteConfigCallback) {
withRateLimitCheck(
RequestType.RemoteConfig,
- userID.hashCode(),
+ (userID + contextKey).hashCode(),
{ error -> callback.onError(error) }
) {
- repository.remoteConfig(userID, callback)
+ repository.remoteConfig(userID, contextKey, callback)
}
}
diff --git a/sdk/src/main/java/com/qonversion/android/sdk/internal/services/QRemoteConfigService.kt b/sdk/src/main/java/com/qonversion/android/sdk/internal/services/QRemoteConfigService.kt
index e7ff5657..4ff9b453 100644
--- a/sdk/src/main/java/com/qonversion/android/sdk/internal/services/QRemoteConfigService.kt
+++ b/sdk/src/main/java/com/qonversion/android/sdk/internal/services/QRemoteConfigService.kt
@@ -9,8 +9,12 @@ import javax.inject.Inject
internal class QRemoteConfigService @Inject constructor(
private val repository: QRepository
) {
- fun loadRemoteConfig(userId: String, callback: QonversionRemoteConfigCallback) {
- repository.remoteConfig(userId, callback)
+ fun loadRemoteConfig(
+ userId: String,
+ contextKey: String?,
+ callback: QonversionRemoteConfigCallback
+ ) {
+ repository.remoteConfig(userId, contextKey, callback)
}
fun attachUserToExperiment(