Skip to content

Commit

Permalink
Release 7.2.0
Browse files Browse the repository at this point in the history
Release 7.2.0
  • Loading branch information
SpertsyanKM authored Mar 8, 2024
2 parents 3dad614 + daa0477 commit 70ed9aa
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 49 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
buildscript {
ext {
release = [
versionName: "7.1.0",
versionName: "7.2.0",
versionCode: 1
]
}
Expand Down
3 changes: 2 additions & 1 deletion config/detekt/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<ID>ComplexMethod:ScreenPresenter.kt$ScreenPresenter$override fun shouldOverrideUrlLoading(url: String?): Boolean</ID>
<ID>ComplexMethod:errors.kt$internal fun BillingError.toQonversionError(): QonversionError</ID>
<ID>ConstructorParameterNaming:Environment.kt$Environment$@Json(name = "app_version") val app_version: String</ID>
<ID>ConstructorParameterNaming:QRemoteConfig.kt$QRemoteConfig$@Json(name = "source") internal val _source: QRemoteConfigurationSource?</ID>
<ID>EmptyCatchBlock:AdvertisingProvider.kt$AdvertisingProvider.AdvertisingConnection${ }</ID>
<ID>EmptyFunctionBlock:AdvertisingProvider.kt$AdvertisingProvider.AdvertisingConnection${}</ID>
<ID>EmptyFunctionBlock:QAutomationsManagerTest.kt$QAutomationsManagerTest.&lt;no name provided&gt;${}</ID>
Expand Down Expand Up @@ -113,6 +112,7 @@
<ID>MaxLineLength:QonversionBillingService.kt$QonversionBillingService${ error -&gt; logger.release("Failed to fetch product type for purchase $productId - " + error.message) }</ID>
<ID>MaxLineLength:QonversionConfig.kt$QonversionConfig.Builder$*</ID>
<ID>MaxLineLength:QonversionError.kt$QonversionErrorCode$*</ID>
<ID>MaxLineLength:QonversionError.kt$QonversionErrorCode$RemoteConfigurationNotAvailable : QonversionErrorCode</ID>
<ID>MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"""HTTP status code=400, data={"message":"Invalid access token received","code":10003,"status":400,"extra":[]}. """</ID>
<ID>MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"lcbfeigohklhpdgmpildjabg.AO-J1OyV-EE2bKGqDcRCvqjZ2NI1uHDRuvonRn5RorP6LNsyK7yHK8FaFlXp6bjTEX3-4JvZKtbY_bpquKBfux09Mfkx05M9YGZsfsr5BJk74r719m77Oyo"</ID>
<ID>MaxLineLength:QonversionRepositoryIntegrationTest.kt$QonversionRepositoryIntegrationTest$"lgeigljfpmeoddkcebkcepjc.AO-J1Oy305qZj99jXTPEVBN8UZGoYAtjDLj4uTjRQvUFaG0vie-nr6VBlN0qnNDMU8eJR-sI7o3CwQyMOEHKl8eJsoQ86KSFzxKBR07PSpHLI_o7agXhNKY"</ID>
Expand Down Expand Up @@ -140,6 +140,7 @@
<ID>MaximumLineLength:com.qonversion.android.sdk.automations.internal.QAutomationsManager.kt:135</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.automations.internal.QAutomationsManager.kt:142</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.automations.mvp.ScreenPresenterTest.kt:159</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.dto.QonversionError.kt:45</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.dto.products.QProductStoreDetails.kt:130</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:213</ID>
<ID>MaximumLineLength:com.qonversion.android.sdk.internal.OutagerIntegrationTest.kt:370</ID>
Expand Down
2 changes: 1 addition & 1 deletion fastlane/report.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@



<testcase classname="fastlane.lanes" name="0: default_platform" time="0.019863">
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000564">

</testcase>

Expand Down
9 changes: 8 additions & 1 deletion sdk/src/main/java/com/qonversion/android/sdk/Qonversion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.squareup.moshi.JsonClass
data class QRemoteConfig internal constructor(
@Json(name = "payload") val payload: Map<String, Any>,
@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!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,84 +13,96 @@ internal class QRemoteConfigManager @Inject constructor(
private val remoteConfigService: QRemoteConfigService,
private val internalConfig: InternalConfig
) {
internal class LoadingState(
var loadedConfig: QRemoteConfig? = null,
val callbacks: MutableList<QonversionRemoteConfigCallback> = mutableListOf(),
var isInProgress: Boolean = false
)

lateinit var userStateProvider: UserStateProvider
private var currentRemoteConfig: QRemoteConfig? = null
private var remoteConfigCallbacks = mutableListOf<QonversionRemoteConfigCallback>()
private var isRequestInProgress: Boolean = false
private var loadingStates = mutableMapOf<String?, LoadingState>()

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)
}

fun attachUserToRemoteConfiguration(
remoteConfigurationId: String,
callback: QonversionRemoteConfigurationAttachCallback
) {
currentRemoteConfig = null
loadingStates[null]?.loadedConfig = null
remoteConfigService.attachUserToRemoteConfiguration(remoteConfigurationId, internalConfig.uid, callback)
}

fun detachUserFromRemoteConfiguration(
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() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,19 @@ 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()}")
val body = it.body()
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 70ed9aa

Please sign in to comment.