diff --git a/build.gradle b/build.gradle index 2515422..59b6f9a 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ buildscript { plugins { id "org.sonarqube" version "3.5.0.2730" id "org.jlleitschuh.gradle.ktlint" version "13.0.0" + id "org.jetbrains.kotlin.plugin.compose" version "2.0.0" } sonarqube { @@ -29,7 +30,7 @@ sonarqube { } } -apply plugin: 'org.jlleitschuh.gradle.ktlint' + apply plugin: "kotlin-android" apply plugin: 'com.mparticle.kit' @@ -48,6 +49,9 @@ android { jvmArgs += ['--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED'] } } + buildFeatures { + compose true + } } repositories { @@ -58,12 +62,16 @@ repositories { dependencies { implementation 'androidx.annotation:annotation:1.5.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation 'androidx.compose.runtime:runtime-android:1.8.3' api 'com.rokt:roktsdk:4.11.0' testImplementation files('libs/java-json.jar') testImplementation 'com.squareup.assertj:assertj-android:1.2.0' testImplementation ("io.mockk:mockk:1.13.4") testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' + compileOnly 'androidx.compose.ui:ui:1.0.0' + compileOnly 'androidx.compose.material:material:1.0.0' + compileOnly 'androidx.compose.ui:ui-tooling:1.0.0' } ktlint { diff --git a/src/main/kotlin/com/mparticle/kits/RoktKit.kt b/src/main/kotlin/com/mparticle/kits/RoktKit.kt index eececa4..5ce6f35 100644 --- a/src/main/kotlin/com/mparticle/kits/RoktKit.kt +++ b/src/main/kotlin/com/mparticle/kits/RoktKit.kt @@ -23,6 +23,7 @@ import com.mparticle.rokt.RoktConfig import com.mparticle.rokt.RoktEmbeddedView import com.rokt.roktsdk.CacheConfig import com.rokt.roktsdk.Rokt +import com.rokt.roktsdk.Rokt.RoktCallback import com.rokt.roktsdk.Rokt.SdkFrameworkType.Android import com.rokt.roktsdk.Rokt.SdkFrameworkType.Cordova import com.rokt.roktsdk.Rokt.SdkFrameworkType.Flutter @@ -30,8 +31,12 @@ import com.rokt.roktsdk.Rokt.SdkFrameworkType.ReactNative import com.rokt.roktsdk.RoktEvent import com.rokt.roktsdk.RoktWidgetDimensionCallBack import com.rokt.roktsdk.Widget +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import java.lang.ref.WeakReference import java.math.BigDecimal @@ -56,7 +61,10 @@ class RoktKit : override fun getInstance(): RoktKit = this + private var deferredAttributes: CompletableDeferred>? = null + public override fun onKitCreate(settings: Map, ctx: Context): List { + register(this) applicationContext = ctx.applicationContext val roktTagId = settings[ROKT_ACCOUNT_ID] if (KitUtils.isEmpty(roktTagId)) { @@ -186,6 +194,23 @@ class RoktKit : }?.toMap() this.mpRoktEventCallback = mpRoktEventCallback + val finalAttributes = prepareFinalAttributes(filterUser, attributes) + val roktConfig = mpRoktConfig?.let { mapToRoktConfig(it) } + Rokt.execute( + viewName, + finalAttributes, + this, + // Pass placeholders and fontTypefaces only if they are not empty or null + placeholders.takeIf { it?.isNotEmpty() == true }, + fontTypefaces.takeIf { it?.isNotEmpty() == true }, + roktConfig, + ) + } + + private fun prepareFinalAttributes( + filterUser: FilteredMParticleUser?, + attributes: Map, + ): Map { val finalAttributes = mutableMapOf() filterUser?.userAttributes?.let { userAttrs -> for ((key, value) in userAttrs) { @@ -203,16 +228,7 @@ class RoktKit : finalAttributes.put(ROKT_ATTRIBUTE_SANDBOX_MODE, value) } verifyHashedEmail(finalAttributes) - val roktConfig = mpRoktConfig?.let { mapToRoktConfig(it) } - Rokt.execute( - viewName, - finalAttributes, - this, - // Pass placeholders and fontTypefaces only if they are not empty or null - placeholders.takeIf { it?.isNotEmpty() == true }, - fontTypefaces.takeIf { it?.isNotEmpty() == true }, - roktConfig, - ) + return finalAttributes } override fun events(identifier: String): Flow = Rokt.events(identifier).map { event -> @@ -274,6 +290,26 @@ class RoktKit : Rokt.close() } + override fun enrichAttributes(attributes: MutableMap, user: FilteredMParticleUser?) { + val finalAttributes = prepareFinalAttributes(user, attributes) + deferredAttributes?.complete(finalAttributes) + } + + suspend fun runComposableWithCallback( + attributes: Map, + mpRoktEventCallback: MpRoktEventCallback?, + onResult: (Map, RoktCallback) -> Unit, + ) { + val instance = MParticle.getInstance() + deferredAttributes = CompletableDeferred() + instance?.Internal()?.kitManager?.prepareAttributesAsync(attributes) + this.mpRoktEventCallback = mpRoktEventCallback + CoroutineScope(Dispatchers.Default).launch { + val resultAttributes = deferredAttributes!!.await() + onResult(resultAttributes, this@RoktKit) + } + } + private fun mapToRoktConfig(config: RoktConfig): com.rokt.roktsdk.RoktConfig { val colorMode = when (config.colorMode) { RoktConfig.ColorMode.LIGHT -> com.rokt.roktsdk.RoktConfig.ColorMode.LIGHT @@ -367,6 +403,14 @@ class RoktKit : } companion object { + @Volatile + var instance: RoktKit? = null + private set + + fun register(kit: RoktKit) { + instance = kit + } + const val NAME = "Rokt" const val ROKT_ACCOUNT_ID = "accountId" const val MPID = "mpid" diff --git a/src/main/kotlin/com/mparticle/kits/RoktLayout.kt b/src/main/kotlin/com/mparticle/kits/RoktLayout.kt new file mode 100644 index 0000000..8b6d810 --- /dev/null +++ b/src/main/kotlin/com/mparticle/kits/RoktLayout.kt @@ -0,0 +1,42 @@ +package com.mparticle.kits + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import com.mparticle.MpRoktEventCallback +import com.rokt.roktsdk.Rokt + +@Composable +@Suppress("FunctionName") +fun RoktLayout( + sdkTriggered: Boolean, + viewName: String, + attributes: Map, + location: String, + modifier: Modifier = Modifier, + mpRoktEventCallback: MpRoktEventCallback? = null, +) { + val instance = RoktKit.instance + val resultMapState = remember { mutableStateOf(null) } + if (sdkTriggered) { + LaunchedEffect(Unit) { + instance?.runComposableWithCallback(attributes, mpRoktEventCallback, { resultMap, callback -> + resultMapState.value = RoktResult(resultMap, callback) + }) + } + } + + resultMapState.value?.let { resultMap -> + com.rokt.roktsdk.RoktLayout( + sdkTriggered, viewName, modifier, resultMap.attributes, location, + onLoad = { resultMap.callback.onLoad() }, + onShouldShowLoadingIndicator = { resultMap.callback.onShouldShowLoadingIndicator() }, + onShouldHideLoadingIndicator = { resultMap.callback.onShouldHideLoadingIndicator() }, + onUnload = { reason -> resultMap.callback.onUnload(reason) }, + ) + } +} + +data class RoktResult(val attributes: Map, val callback: Rokt.RoktCallback)