diff --git a/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java b/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java index f26e4f3b0..5ec657866 100644 --- a/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java +++ b/android-core/src/main/java/com/mparticle/internal/KitFrameworkWrapper.java @@ -26,6 +26,7 @@ import com.mparticle.identity.IdentityApiRequest; import com.mparticle.identity.MParticleUser; import com.mparticle.internal.listeners.InternalListenerManager; +import com.mparticle.rokt.PlacementOptions; import com.mparticle.rokt.RoktConfig; import com.mparticle.rokt.RoktEmbeddedView; import com.mparticle.rokt.RoktOptions; @@ -685,6 +686,25 @@ public void execute(@NonNull String viewName, } } + @Override + public void execute(@NonNull String identifier, + @NonNull Map attributes, + @Nullable MpRoktEventCallback mpRoktEventCallback, + @Nullable Map> embeddedViews, + @Nullable Map> fontTypefaces, + @Nullable RoktConfig config, + @Nullable PlacementOptions options) { + if (mKitManager != null) { + mKitManager.execute(identifier, + attributes, + mpRoktEventCallback, + embeddedViews, + fontTypefaces, + config, + options); + } + } + @Override public Flow events(@NonNull String identifier) { if (mKitManager != null) { @@ -836,4 +856,4 @@ public void onKitApiCalled(String methodName, int kitId, Boolean used, Object... } }; } -} \ No newline at end of file +} diff --git a/android-core/src/main/java/com/mparticle/internal/KitManager.java b/android-core/src/main/java/com/mparticle/internal/KitManager.java index 716f526d2..ad31c550c 100644 --- a/android-core/src/main/java/com/mparticle/internal/KitManager.java +++ b/android-core/src/main/java/com/mparticle/internal/KitManager.java @@ -22,6 +22,7 @@ import com.mparticle.consent.ConsentState; import com.mparticle.identity.IdentityApiRequest; import com.mparticle.identity.MParticleUser; +import com.mparticle.rokt.PlacementOptions; import com.mparticle.rokt.RoktConfig; import com.mparticle.rokt.RoktEmbeddedView; import com.mparticle.rokt.RoktOptions; @@ -140,6 +141,14 @@ void execute(@NonNull String identifier, @Nullable Map> fontTypefaces, @Nullable RoktConfig config); + void execute(@NonNull String identifier, + @NonNull Map attributes, + @Nullable MpRoktEventCallback mpRoktEventCallback, + @Nullable Map> embeddedViews, + @Nullable Map> fontTypefaces, + @Nullable RoktConfig config, + @Nullable PlacementOptions options); + Flow events(@NonNull String identifier); void setWrapperSdkVersion(@NonNull WrapperSdkVersion wrapperSdkVersion); diff --git a/android-core/src/main/kotlin/com/mparticle/Rokt.kt b/android-core/src/main/kotlin/com/mparticle/Rokt.kt index 58a70543e..ca5dd0ec8 100644 --- a/android-core/src/main/kotlin/com/mparticle/Rokt.kt +++ b/android-core/src/main/kotlin/com/mparticle/Rokt.kt @@ -4,6 +4,7 @@ import android.graphics.Typeface import com.mparticle.internal.ConfigManager import com.mparticle.internal.KitManager import com.mparticle.internal.listeners.ApiClass +import com.mparticle.rokt.PlacementOptions import com.mparticle.rokt.RoktConfig import com.mparticle.rokt.RoktEmbeddedView import kotlinx.coroutines.flow.Flow @@ -22,7 +23,8 @@ class Rokt internal constructor(private val mConfigManager: ConfigManager, priva config: RoktConfig? = null, ) { if (mConfigManager.isEnabled) { - mKitManager.execute(identifier, HashMap(attributes), callbacks, embeddedViews, fontTypefaces, config) + val placementOptions = PlacementOptions(integrationStartTimestamp = System.currentTimeMillis()) + mKitManager.execute(identifier, HashMap(attributes), callbacks, embeddedViews, fontTypefaces, config, placementOptions) } } diff --git a/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt b/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt new file mode 100644 index 000000000..e4420ee38 --- /dev/null +++ b/android-core/src/main/kotlin/com/mparticle/rokt/PlacementOptions.kt @@ -0,0 +1,3 @@ +package com.mparticle.rokt + +data class PlacementOptions(var integrationStartTimestamp: Long) diff --git a/android-core/src/test/kotlin/com/mparticle/RoktTest.kt b/android-core/src/test/kotlin/com/mparticle/RoktTest.kt index eac5c3d2b..2981cc3e9 100644 --- a/android-core/src/test/kotlin/com/mparticle/RoktTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/RoktTest.kt @@ -5,6 +5,7 @@ import android.os.Looper import android.os.SystemClock import com.mparticle.internal.ConfigManager import com.mparticle.internal.KitManager +import com.mparticle.rokt.PlacementOptions import com.mparticle.rokt.RoktConfig import com.mparticle.rokt.RoktEmbeddedView import kotlinx.coroutines.flow.Flow @@ -14,7 +15,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isNull import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -81,7 +85,7 @@ class RoktTest { config = config, ) - verify(kitManager)?.execute("testView", attributes, callbacks, placeholders, fonts, config) + verify(kitManager)?.execute(eq("testView"), eq(attributes), eq(callbacks), eq(placeholders), eq(fonts), eq(config), any()) } @Test @@ -93,7 +97,7 @@ class RoktTest { rokt.selectPlacements(attributes = attributes, identifier = "basicView") - verify(kitManager).execute("basicView", attributes, null, null, null, null) + verify(kitManager).execute(eq("basicView"), eq(attributes), isNull(), isNull(), isNull(), isNull(), any()) } @Test @@ -105,7 +109,7 @@ class RoktTest { attributes = HashMap(), ) - verify(kitManager, never()).execute(any(), any(), any(), any(), any(), any()) + verify(kitManager, never()).execute(any(), any(), any(), any(), any(), any(), any()) } @Test @@ -169,4 +173,30 @@ class RoktTest { assertTrue(elements.isEmpty()) } } + + @Test + fun testSelectPlacements_withOptions_whenEnabled() { + `when`(configManager.isEnabled).thenReturn(true) + val currentTimeMillis = System.currentTimeMillis() + + val attributes = mutableMapOf() + + rokt.selectPlacements( + identifier = "testView", + attributes = attributes, + ) + + // Verify call is forwarded + val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) + verify(kitManager).execute( + eq("testView"), + eq(HashMap(attributes)), + isNull(), + isNull(), + isNull(), + isNull(), + optionsCaptor.capture(), + ) + assertTrue(optionsCaptor.value.integrationStartTimestamp >= currentTimeMillis) + } } diff --git a/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java b/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java index 878889e1d..7102feb63 100644 --- a/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java +++ b/android-kit-base/src/main/java/com/mparticle/kits/KitIntegration.java @@ -21,6 +21,7 @@ import com.mparticle.commerce.CommerceEvent; import com.mparticle.consent.ConsentState; import com.mparticle.identity.MParticleUser; +import com.mparticle.rokt.PlacementOptions; import com.mparticle.rokt.RoktConfig; import com.mparticle.rokt.RoktEmbeddedView; @@ -626,6 +627,15 @@ void execute(@NonNull String viewName, @Nullable FilteredMParticleUser user, @Nullable RoktConfig config); + void execute(@NonNull String viewName, + @NonNull Map attributes, + @Nullable MpRoktEventCallback mpRoktEventCallback, + @Nullable Map> placeHolders, + @Nullable Map> fontTypefaces, + @Nullable FilteredMParticleUser user, + @Nullable RoktConfig config, + @Nullable PlacementOptions options); + Flow events(@NonNull String identifier); void enrichAttributes( diff --git a/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java b/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java index 9c95e62d4..9cba75f50 100644 --- a/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java +++ b/android-kit-base/src/main/java/com/mparticle/kits/KitManagerImpl.java @@ -45,6 +45,7 @@ import com.mparticle.internal.MPUtility; import com.mparticle.internal.ReportingManager; import com.mparticle.kits.mappings.CustomMapping; +import com.mparticle.rokt.PlacementOptions; import com.mparticle.rokt.RoktConfig; import com.mparticle.rokt.RoktEmbeddedView; import com.mparticle.rokt.RoktOptions; @@ -1353,6 +1354,17 @@ public void execute(@NonNull String viewName, @Nullable Map> placeHolders, @Nullable Map> fontTypefaces, @Nullable RoktConfig config) { + execute(viewName, attributes, mpRoktEventCallback, placeHolders, fontTypefaces, config, null); + } + + @Override + public void execute(@NonNull String viewName, + @NonNull Map attributes, + @Nullable MpRoktEventCallback mpRoktEventCallback, + @Nullable Map> placeHolders, + @Nullable Map> fontTypefaces, + @Nullable RoktConfig config, + @Nullable PlacementOptions options) { for (KitIntegration provider : providers.values()) { try { if (provider instanceof KitIntegration.RoktListener && !provider.isDisabled()) { @@ -1374,7 +1386,8 @@ public void execute(@NonNull String viewName, placeHolders, fontTypefaces, FilteredMParticleUser.getInstance(user.getId(), provider), - config); + config, + options); }); } } catch (Exception e) { diff --git a/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt b/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt index 699ac3b1a..dc32a0434 100644 --- a/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt +++ b/android-kit-base/src/test/kotlin/com/mparticle/kits/KitManagerImplTest.kt @@ -29,6 +29,7 @@ import com.mparticle.mock.MockContext import com.mparticle.mock.MockKitConfiguration import com.mparticle.mock.MockKitManagerImpl import com.mparticle.mock.MockMParticle +import com.mparticle.rokt.PlacementOptions import com.mparticle.rokt.RoktConfig import com.mparticle.rokt.RoktEmbeddedView import com.mparticle.testutils.TestingUtils @@ -43,10 +44,12 @@ import org.json.JSONException import org.json.JSONObject import org.junit.Assert import org.junit.Assert.assertNull +import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.Mockito import org.mockito.Mockito.mock @@ -1848,6 +1851,77 @@ class KitManagerImplTest { Assert.assertEquals("false", attributes["sandbox"]) } + @Test + fun testRokt_execute_with_PlacementOptions() { + val mockUser = mock(MParticleUser::class.java) + `when`(mockIdentity!!.currentUser).thenReturn(mockUser) + + val manager: KitManagerImpl = MockKitManagerImpl() + val roktListener = + mock( + KitIntegration::class.java, + withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), + ) + `when`(roktListener.isDisabled).thenReturn(false) + manager.providers = + ConcurrentHashMap().apply { + put(1, roktListener) + } + + val attributes = hashMapOf() + val placementOptions = PlacementOptions(integrationStartTimestamp = 123L) + + manager.execute("Test", attributes, null, null, null, null, placementOptions) + + val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) + verify(roktListener as KitIntegration.RoktListener).execute( + any(), + any(), + any(), + any(), + any(), + any(), + any(), + optionsCaptor.capture(), + ) + assertSame(placementOptions, optionsCaptor.value) + } + + @Test + fun testRokt_execute_without_PlacementOptions() { + val mockUser = mock(MParticleUser::class.java) + `when`(mockIdentity!!.currentUser).thenReturn(mockUser) + + val manager: KitManagerImpl = MockKitManagerImpl() + val roktListener = + mock( + KitIntegration::class.java, + withSettings().extraInterfaces(KitIntegration.RoktListener::class.java), + ) + `when`(roktListener.isDisabled).thenReturn(false) + manager.providers = + ConcurrentHashMap().apply { + put(1, roktListener) + } + + val attributes = hashMapOf() + + manager.execute("Test", attributes, null, null, null, null) + + val optionsCaptor = ArgumentCaptor.forClass(PlacementOptions::class.java) + verify(roktListener as KitIntegration.RoktListener).execute( + any(), + any(), + any(), + any(), + any(), + any(), + any(), + optionsCaptor.capture(), + ) + assertNull(optionsCaptor.value) + } + @Test fun testRokt_SandboxMode_When_Environment_IS_Development() { val sideloadedKit = mock(MPSideloadedKit::class.java) @@ -2265,6 +2339,19 @@ class KitManagerImplTest { Logger.info("Executed with $attributes") } + override fun execute( + viewName: String, + attributes: MutableMap, + mpRoktEventCallback: MpRoktEventCallback?, + placeHolders: MutableMap>?, + fontTypefaces: MutableMap>?, + user: FilteredMParticleUser?, + config: RoktConfig?, + options: PlacementOptions?, + ) { + Logger.info("Executed with $attributes and options $options") + } + override fun events(identifier: String): Flow { Logger.info("events called with identfier: $identifier") return flowOf()