From 48f880b8d554e41194def617deb569b6f9aa9d3c Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 9 Jun 2026 13:52:15 +0100 Subject: [PATCH 1/3] Matching ios appAlreadyRunning parameter 1-to-1 --- .../com/iterable/iterableapi/IterableApi.java | 18 +++++++++++++++--- .../iterableapi/IterableApiClient.java | 3 ++- .../iterableapi/IterableConstants.java | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java index 3f45cafaa..a49ab8286 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java @@ -1274,7 +1274,7 @@ public void registerDeviceToken(@NonNull String deviceToken) { } public void trackPushOpen(int campaignId, int templateId, @NonNull String messageId) { - queueOrExecute(() -> trackPushOpen(campaignId, templateId, messageId, null), "trackPushOpen(" + campaignId + ", " + templateId + ", " + maskPII(messageId) + ")"); + trackPushOpen(campaignId, templateId, messageId, false, null); } /** @@ -1283,14 +1283,26 @@ public void trackPushOpen(int campaignId, int templateId, @NonNull String messag * @param templateId */ public void trackPushOpen(int campaignId, int templateId, @NonNull String messageId, @Nullable JSONObject dataFields) { + trackPushOpen(campaignId, templateId, messageId, false, dataFields); + } + + /** + * Tracks when a push notification is opened on device. + * @param campaignId + * @param templateId + * @param messageId + * @param appAlreadyRunning Whether the app was already running when the push was received. + * @param dataFields + */ + public void trackPushOpen(int campaignId, int templateId, @NonNull String messageId, boolean appAlreadyRunning, @Nullable JSONObject dataFields) { queueOrExecute(() -> { if (messageId == null) { IterableLogger.e(TAG, "messageId is null"); return; } - apiClient.trackPushOpen(campaignId, templateId, messageId, dataFields); - }, "trackPushOpen(" + campaignId + ", " + templateId + ", " + maskPII(messageId) + ", dataFields)"); + apiClient.trackPushOpen(campaignId, templateId, messageId, appAlreadyRunning, dataFields); + }, "trackPushOpen(" + campaignId + ", " + templateId + ", " + maskPII(messageId) + ", " + appAlreadyRunning + ", dataFields)"); } /** diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java index 924ebc24e..e8e685b5e 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java @@ -587,13 +587,14 @@ public void trackEmbeddedSession(@NonNull IterableEmbeddedSession session) { } } - protected void trackPushOpen(int campaignId, int templateId, @NonNull String messageId, @Nullable JSONObject dataFields) { + protected void trackPushOpen(int campaignId, int templateId, @NonNull String messageId, boolean appAlreadyRunning, @Nullable JSONObject dataFields) { JSONObject requestJSON = new JSONObject(); try { if (dataFields == null) { dataFields = new JSONObject(); } + dataFields.put(IterableConstants.KEY_APP_ALREADY_RUNNING, appAlreadyRunning); addEmailOrUserIdToJson(requestJSON); requestJSON.put(IterableConstants.KEY_CAMPAIGN_ID, campaignId); diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java index eb2d3fc4d..c3ac914a4 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableConstants.java @@ -22,6 +22,7 @@ public final class IterableConstants { public static final String HEADER_SDK_AUTHORIZATION = "Authorization"; public static final String HEADER_SDK_AUTH_FORMAT = "Bearer "; public static final String HEADER_SDK_PROCESSOR_TYPE = "SDK-Request-Processor"; + public static final String KEY_APP_ALREADY_RUNNING = "appAlreadyRunning"; public static final String KEY_APPLICATION_NAME = "applicationName"; public static final String KEY_CAMPAIGN_ID = "campaignId"; public static final String KEY_CURRENT_EMAIL = "currentEmail"; From 0c7fcec876f800f783c87b197ef096a6cc73075c Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 9 Jun 2026 13:52:43 +0100 Subject: [PATCH 2/3] Changelog entry for appAlreadyRunning --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e4cc5a7..d3919d550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - Added support for in-app messages in fully Jetpack Compose apps using a Dialog-based renderer (`IterableInAppDialogNotification`), removing the requirement for a `FragmentActivity`. +- Added `appAlreadyRunning` field to `trackPushOpen`. New `trackPushOpen(int, int, String, boolean, JSONObject)` overload sends the value through; existing overloads default to `false`. ## [3.8.0] ### Added From 8e238e102408a9cfc42122b295ec638f2a8bc3bf Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Wed, 17 Jun 2026 16:27:08 +0100 Subject: [PATCH 3/3] Tests for required paths --- .../iterable/iterableapi/IterableApiTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java index 04303edd8..a8ee54116 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java @@ -499,6 +499,43 @@ public void testInAppOpen() throws Exception { assertEquals("testMessageId", requestJson.getString(IterableConstants.KEY_MESSAGE_ID)); } + @Test + public void testTrackPushOpenDefaultsAppAlreadyRunningToFalse() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + + IterableApi.initialize(getContext(), "apiKey", new IterableConfig.Builder().setAutoPushRegistration(false).build()); + IterableApi.getInstance().setEmail("test@email.com"); + IterableApi.getInstance().trackPushOpen(1234, 4321, "testMessageId"); + shadowOf(getMainLooper()).idle(); + + RecordedRequest trackPushOpenRequest = server.takeRequest(1, TimeUnit.SECONDS); + assertNotNull(trackPushOpenRequest); + assertEquals("/" + IterableConstants.ENDPOINT_TRACK_PUSH_OPEN, trackPushOpenRequest.getPath()); + JSONObject requestJson = new JSONObject(trackPushOpenRequest.getBody().readUtf8()); + assertEquals(1234, requestJson.getInt(IterableConstants.KEY_CAMPAIGN_ID)); + assertEquals(4321, requestJson.getInt(IterableConstants.KEY_TEMPLATE_ID)); + assertEquals("testMessageId", requestJson.getString(IterableConstants.KEY_MESSAGE_ID)); + assertEquals(false, requestJson.getJSONObject(IterableConstants.KEY_DATA_FIELDS).getBoolean(IterableConstants.KEY_APP_ALREADY_RUNNING)); + } + + @Test + public void testTrackPushOpenSendsAppAlreadyRunningAndPreservesDataFields() throws Exception { + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + + IterableApi.initialize(getContext(), "apiKey", new IterableConfig.Builder().setAutoPushRegistration(false).build()); + IterableApi.getInstance().setEmail("test@email.com"); + IterableApi.getInstance().trackPushOpen(1234, 4321, "testMessageId", true, new JSONObject("{\"key\": \"value\"}")); + shadowOf(getMainLooper()).idle(); + + RecordedRequest trackPushOpenRequest = server.takeRequest(1, TimeUnit.SECONDS); + assertNotNull(trackPushOpenRequest); + assertEquals("/" + IterableConstants.ENDPOINT_TRACK_PUSH_OPEN, trackPushOpenRequest.getPath()); + JSONObject requestJson = new JSONObject(trackPushOpenRequest.getBody().readUtf8()); + JSONObject dataFields = requestJson.getJSONObject(IterableConstants.KEY_DATA_FIELDS); + assertEquals(true, dataFields.getBoolean(IterableConstants.KEY_APP_ALREADY_RUNNING)); + assertEquals("value", dataFields.getString("key")); + } + @Test public void testInAppOpenExtended() throws Exception { server.enqueue(new MockResponse().setResponseCode(200).setBody("{}"));