diff --git a/app/build.gradle b/app/build.gradle index 8c74f9670..e31077eb5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,10 @@ android { multiDexEnabled true } + buildFeatures { + buildConfig true + } + buildTypes { release { minifyEnabled true diff --git a/build.gradle b/build.gradle index 8af279fbb..3d1e16f3c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.9.0' + ext.kotlin_version = '1.9.22' ext.mavenPublishEnabled = true repositories { google() - jcenter() + mavenCentral() maven { url "https://plugins.gradle.org/m2/" } @@ -26,7 +26,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/iterableapi-ui/build.gradle b/iterableapi-ui/build.gradle index 28014424e..4b1cefd4f 100644 --- a/iterableapi-ui/build.gradle +++ b/iterableapi-ui/build.gradle @@ -23,6 +23,10 @@ android { targetCompatibility JavaVersion.VERSION_17 } + kotlinOptions { + jvmTarget = "17" + } + publishing { multipleVariants { allVariants() @@ -76,8 +80,10 @@ task javadoc(type: Javadoc) { } // A hack to import the classpath and BuildConfig into the javadoc task -afterEvaluate { - javadoc.classpath += files(android.libraryVariants.collect { variant -> variant.javaCompile.classpath.files }) - javadoc.classpath += files(android.libraryVariants.collect { variant -> "build/generated/source/r/${variant.flavorName}/${variant.buildType.name}" }) - javadoc.classpath += files(android.libraryVariants.collect { variant -> "build/generated/source/buildConfig/${variant.flavorName}/${variant.buildType.name}" }) +android.libraryVariants.configureEach { variant -> + javadoc.doFirst { + javadoc.classpath += files(variant.javaCompileProvider.get().classpath.files) + javadoc.classpath += files("build/generated/source/r/${variant.flavorName}/${variant.buildType.name}") + javadoc.classpath += files("build/generated/source/buildConfig/${variant.flavorName}/${variant.buildType.name}") + } } diff --git a/iterableapi/build.gradle b/iterableapi/build.gradle index b6a6e9d97..64965cc5b 100644 --- a/iterableapi/build.gradle +++ b/iterableapi/build.gradle @@ -6,7 +6,6 @@ android { compileSdk 34 namespace 'com.iterable.iterableapi' - testNamespace 'iterable.com.iterableapi' buildFeatures { buildConfig = true @@ -17,6 +16,10 @@ android { targetCompatibility JavaVersion.VERSION_17 } + kotlinOptions { + jvmTarget = "17" + } + defaultConfig { minSdkVersion 21 targetSdkVersion 34 @@ -114,8 +117,10 @@ task checkstyle(type: Checkstyle) { } // A hack to import the classpath and BuildConfig into the javadoc task -afterEvaluate { - javadoc.classpath += files(android.libraryVariants.collect { variant -> variant.javaCompile.classpath.files }) - javadoc.classpath += files(android.libraryVariants.collect { variant -> "build/generated/source/r/${variant.flavorName}/${variant.buildType.name}" }) - javadoc.classpath += files(android.libraryVariants.collect { variant -> "build/generated/source/buildConfig/${variant.flavorName}/${variant.buildType.name}" }) +android.libraryVariants.configureEach { variant -> + javadoc.doFirst { + javadoc.classpath += files(variant.javaCompileProvider.get().classpath.files) + javadoc.classpath += files("build/generated/source/r/${variant.flavorName}/${variant.buildType.name}") + javadoc.classpath += files("build/generated/source/buildConfig/${variant.flavorName}/${variant.buildType.name}") + } } \ No newline at end of file diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableActionRunner.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableActionRunner.java index 4476709d0..bf1f341a1 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableActionRunner.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableActionRunner.java @@ -13,7 +13,7 @@ import java.util.List; -class IterableActionRunner { +public class IterableActionRunner { @VisibleForTesting static IterableActionRunnerImpl instance = new IterableActionRunnerImpl(); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt index 8b53171bc..1d9edf97f 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedManager.kt @@ -163,7 +163,7 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback } } - fun handleEmbeddedClick(message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String?) { + public fun handleEmbeddedClick(message: IterableEmbeddedMessage, buttonIdentifier: String?, clickedUrl: String?) { if ((clickedUrl != null) && clickedUrl.toString().isNotEmpty()) { if (clickedUrl.startsWith(IterableConstants.URL_SCHEME_ACTION)) { // This is an action:// URL, pass that to the custom action handler diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedPlacement.kt b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedPlacement.kt index 4d71ff55b..a27018de5 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedPlacement.kt +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableEmbeddedPlacement.kt @@ -46,7 +46,7 @@ data class IterableEmbeddedPlacement( } } -data class IterableEmbeddedMessage ( +public data class IterableEmbeddedMessage ( val metadata: EmbeddedMessageMetadata, val elements: EmbeddedMessageElements? = null, val payload: JSONObject? = null @@ -82,7 +82,7 @@ data class IterableEmbeddedMessage ( } } -class EmbeddedMessageMetadata( +public class EmbeddedMessageMetadata( var messageId: String, val placementId: Long, val campaignId: Int? = null, @@ -117,7 +117,7 @@ class EmbeddedMessageMetadata( } } -class EmbeddedMessageElements ( +public class EmbeddedMessageElements ( val title: String? = null, val body: String? = null, val mediaURL: String? = null, @@ -213,7 +213,7 @@ class EmbeddedMessageElements ( } } -class EmbeddedMessageElementsButton ( +public class EmbeddedMessageElementsButton ( val id: String, val title: String? = null, val action: EmbeddedMessageElementsButtonAction? = null @@ -258,7 +258,7 @@ class EmbeddedMessageElementsButton ( } } -class EmbeddedMessageElementsDefaultAction ( +public class EmbeddedMessageElementsDefaultAction ( val type: String, val data: String ) { @@ -287,7 +287,7 @@ class EmbeddedMessageElementsDefaultAction ( } } -class EmbeddedMessageElementsButtonAction ( +public class EmbeddedMessageElementsButtonAction ( val type: String, val data: String ) { @@ -315,7 +315,7 @@ class EmbeddedMessageElementsButtonAction ( } } } -class EmbeddedMessageElementsText ( +public class EmbeddedMessageElementsText ( val id: String, val text: String? = null, val label: String? = null diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableFirebaseMessagingService.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableFirebaseMessagingService.java index 72df83464..54ed5aa9b 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableFirebaseMessagingService.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableFirebaseMessagingService.java @@ -78,6 +78,7 @@ public static boolean handleMessageReceived(@NonNull Context context, @NonNull R String messageId = extras.getString("messageId"); if (messageId != null) { IterableApi.getInstance().getInAppManager().removeMessage(messageId); + IterableApi.getInstance().getInAppManager().syncInApp(); } break; case "UpdateEmbedded": diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java index 9a8589baf..ab18828b6 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java @@ -321,6 +321,8 @@ public void handleInAppClick(@NonNull IterableInAppMessage message, @Nullable Ur synchronized void removeMessage(String messageId) { IterableInAppMessage message = storage.getMessage(messageId); if (message != null) { + // Mark message as consumed before removing it to prevent it from being displayed + message.setConsumed(true); storage.removeMessage(message); } notifyOnChange(); @@ -365,7 +367,10 @@ private void syncWithRemoteQueue(List remoteQueue) { for (IterableInAppMessage localMessage : storage.getMessages()) { if (!remoteQueueMap.containsKey(localMessage.getMessageId())) { + // Mark message as consumed before removing it to prevent it from being displayed + localMessage.setConsumed(true); storage.removeMessage(localMessage); + api.inAppConsume(localMessage, null, null, null, null); changed = true; } diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java index 3dd8a191c..54127c041 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java @@ -18,6 +18,10 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; public class IterableInAppManagerSyncTest extends BaseTest { @@ -81,4 +85,37 @@ public void testSyncOnLogin() throws Exception { verify(inAppManagerMock).syncInApp(); } -} + @Test + public void testRecalledMessagesAreConsumed() throws Exception { + // Create a test message in local storage + IterableInAppMessage testMessage = InAppTestUtils.getTestInboxInAppWithId("test-message-1"); + doReturn(testMessage).when(storageMock).getMessage("test-message-1"); + + // Create a storage with only this message + doReturn(Arrays.asList(testMessage)).when(storageMock).getMessages(); + + // Setup the API to return empty message list (simulating recall) + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + IterableHelper.IterableActionHandler handler = invocation.getArgument(1); + // Return an empty message list + handler.execute("{\"" + IterableConstants.ITERABLE_IN_APP_MESSAGE + "\": []}"); + return null; + } + }).when(iterableApiMock).getInAppMessages(any(Integer.class), any(IterableHelper.IterableActionHandler.class)); + + // Verify message is not consumed initially + assertFalse(testMessage.isConsumed()); + + // Sync with remote queue + inAppManager.syncInApp(); + + // Verify that the message was marked as consumed + assertTrue(testMessage.isConsumed()); + + // Verify that inAppConsume was called + verify(iterableApiMock).inAppConsume(testMessage, null, null, null, null); + } + +} \ No newline at end of file