Skip to content

Commit 3ff73bd

Browse files
Changed for new ENUM type instead of a new handler with similar name to avoid confusion
1 parent 3b1d36a commit 3ff73bd

9 files changed

Lines changed: 80 additions & 117 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
66
### Added
7-
- Added `IterableInAppDisplayHandler` for real-time, per-message control over whether the SDK automatically displays an in-app message. Set it via `IterableConfig.Builder.setInAppDisplayHandler()`. Returning `true` from `isAutoDisplayPaused(message)` defers that message so it is reconsidered on a later display pass (rather than being permanently skipped). The handler takes precedence over the global `IterableInAppManager.setAutoDisplayPaused(boolean)` flag.
7+
- Added a `DEFER` response to `IterableInAppHandler.InAppResponse`, returned from `onNewInApp(message)`. Unlike `SKIP` (which permanently drops the message), `DEFER` leaves the message pending so the SDK reconsiders it on a later display pass (next foreground, sync, or newly arrived message). Use it for temporary, per-message suppression — for example while a splash screen is showing. Existing handlers returning `SHOW`/`SKIP` are unaffected.
88
- Added `IterableInAppManager.resumeInAppDisplay()` so apps can prompt the SDK to re-evaluate pending in-app messages once they become ready to display (e.g. after a splash screen is dismissed), without waiting for the next foreground/sync trigger.
99
- Notification small-icon resolution now falls back through standard conventions — the Firebase `com.google.firebase.messaging.default_notification_icon` meta-data, `@drawable/notification_icon` (Expo / React Native), and `@drawable/ic_notification` — before defaulting to the app launcher icon. This fixes white-square notification icons on Android 5.0+ for apps that configure their icon through these conventions but don't set `iterable_notification_icon`.
1010
- Added support for in-app messages in fully Jetpack Compose apps using a Dialog-based renderer (`IterableInAppDialogNotification`), removing the requirement for a `FragmentActivity`.
@@ -20,6 +20,23 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2020
### Fixed
2121
- Fixed a `TransactionTooLargeException` crash when displaying in-app messages with oversized HTML payloads. The HTML is no longer serialized into the fragment's saved instance state; it is reloaded from storage on recreation. In-apps with missing HTML now dismiss gracefully without registering tracking events, and a warning is logged for HTML payloads exceeding the recommended size.
2222

23+
### Migration guide
24+
**No action required.** Existing `IterableInAppHandler` implementations returning `SHOW`/`SKIP` are unaffected.
25+
26+
To suppress an in-app temporarily (e.g. during a splash screen), return the new `DEFER` instead of `SKIP` — the message stays pending and is re-offered on a later display pass:
27+
28+
```java
29+
new IterableConfig.Builder().setInAppHandler(message ->
30+
appIsShowingSplashScreen()
31+
? IterableInAppHandler.InAppResponse.DEFER
32+
: IterableInAppHandler.InAppResponse.SHOW
33+
).build();
34+
```
35+
36+
Once ready, call `IterableApi.getInstance().getInAppManager().resumeInAppDisplay()` to re-check pending messages immediately instead of waiting for the next foreground/sync.
37+
38+
> **Kotlin:** add a `DEFER` branch to any exhaustive `when` over `InAppResponse`.
39+
2340
## [3.8.0]
2441
### Added
2542
- New `IterableInAppDisplayMode` enum to control how in-app messages interact with system bars. Configure via `IterableConfig.Builder.setInAppDisplayMode()`:

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,6 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
828828
sharedInstance.inAppManager = new IterableInAppManager(
829829
sharedInstance,
830830
sharedInstance.config.inAppHandler,
831-
sharedInstance.config.inAppDisplayHandler,
832831
sharedInstance.config.inAppDisplayInterval,
833832
sharedInstance.config.useInMemoryStorageForInApps);
834833
}

iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,6 @@ public class IterableConfig {
5050
*/
5151
final IterableInAppHandler inAppHandler;
5252

53-
/**
54-
* Optional handler that gives the app real-time, per-message control over whether an in-app
55-
* message should be displayed automatically. Takes precedence over the global auto-display pause.
56-
*/
57-
@Nullable
58-
final IterableInAppDisplayHandler inAppDisplayHandler;
59-
6053
/**
6154
* The number of seconds to wait before showing the next in-app message, if there are multiple
6255
* messages in the queue
@@ -178,7 +171,6 @@ private IterableConfig(Builder builder) {
178171
checkForDeferredDeeplink = builder.checkForDeferredDeeplink;
179172
logLevel = builder.logLevel;
180173
inAppHandler = builder.inAppHandler;
181-
inAppDisplayHandler = builder.inAppDisplayHandler;
182174
inAppDisplayInterval = builder.inAppDisplayInterval;
183175
authHandler = builder.authHandler;
184176
expiringAuthTokenRefreshPeriod = builder.expiringAuthTokenRefreshPeriod;
@@ -208,7 +200,6 @@ public static class Builder {
208200
private boolean checkForDeferredDeeplink;
209201
private int logLevel = Log.ERROR;
210202
private IterableInAppHandler inAppHandler = new IterableDefaultInAppHandler();
211-
private IterableInAppDisplayHandler inAppDisplayHandler = null;
212203
private double inAppDisplayInterval = 30.0;
213204
private IterableAuthHandler authHandler;
214205
private long expiringAuthTokenRefreshPeriod = 60000L;
@@ -318,20 +309,6 @@ public Builder setInAppHandler(@NonNull IterableInAppHandler inAppHandler) {
318309
return this;
319310
}
320311

321-
/**
322-
* Set an in-app display handler for real-time, per-message control over whether an in-app
323-
* message should be displayed automatically. Returning {@code true} from
324-
* {@link IterableInAppDisplayHandler#isAutoDisplayPaused(IterableInAppMessage)} defers the
325-
* message so it is reconsidered on a later display pass. Takes precedence over
326-
* {@link IterableInAppManager#setAutoDisplayPaused(boolean)}.
327-
* @param inAppDisplayHandler In-app display handler provided by the app
328-
*/
329-
@NonNull
330-
public Builder setInAppDisplayHandler(@NonNull IterableInAppDisplayHandler inAppDisplayHandler) {
331-
this.inAppDisplayHandler = inAppDisplayHandler;
332-
return this;
333-
}
334-
335312
/**
336313
* Set the in-app message display interval: the number of seconds to wait before showing
337314
* the next in-app message, if there are multiple messages in the queue

iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppDisplayHandler.java

Lines changed: 0 additions & 20 deletions
This file was deleted.

iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppHandler.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44

55
public interface IterableInAppHandler {
66
enum InAppResponse {
7+
/** Display the in-app message now. */
78
SHOW,
8-
SKIP
9+
/** Do not display the in-app message; it is marked processed and will not be reconsidered. */
10+
SKIP,
11+
/**
12+
* Do not display the in-app message right now, but leave it pending so the SDK asks again on
13+
* a later display pass (e.g. the next foreground, sync, or newly arrived message). Use this
14+
* for temporary, per-message suppression — for example while a splash screen is showing.
15+
*/
16+
DEFER
917
}
1018

1119
@NonNull

iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ public interface Listener {
4343
private final Context context;
4444
private final IterableInAppStorage storage;
4545
private final IterableInAppHandler handler;
46-
@Nullable
47-
private final IterableInAppDisplayHandler displayHandler;
4846
private final IterableInAppDisplayer displayer;
4947
private final IterableActivityMonitor activityMonitor;
5048
private final double inAppDisplayInterval;
@@ -53,10 +51,9 @@ public interface Listener {
5351
private long lastInAppShown = 0;
5452
private boolean autoDisplayPaused = false;
5553

56-
IterableInAppManager(IterableApi iterableApi, IterableInAppHandler handler, @Nullable IterableInAppDisplayHandler displayHandler, double inAppDisplayInterval, boolean useInMemoryStorageForInApps) {
54+
IterableInAppManager(IterableApi iterableApi, IterableInAppHandler handler, double inAppDisplayInterval, boolean useInMemoryStorageForInApps) {
5755
this(iterableApi,
5856
handler,
59-
displayHandler,
6057
inAppDisplayInterval,
6158
IterableInAppManager.getInAppStorageModel(iterableApi, useInMemoryStorageForInApps),
6259
IterableActivityMonitor.getInstance(),
@@ -66,15 +63,13 @@ public interface Listener {
6663
@VisibleForTesting
6764
IterableInAppManager(IterableApi iterableApi,
6865
IterableInAppHandler handler,
69-
@Nullable IterableInAppDisplayHandler displayHandler,
7066
double inAppDisplayInterval,
7167
IterableInAppStorage storage,
7268
IterableActivityMonitor activityMonitor,
7369
IterableInAppDisplayer displayer) {
7470
this.api = iterableApi;
7571
this.context = iterableApi.getMainActivityContext();
7672
this.handler = handler;
77-
this.displayHandler = displayHandler;
7873
this.inAppDisplayInterval = inAppDisplayInterval;
7974
this.storage = storage;
8075
this.displayer = displayer;
@@ -164,12 +159,13 @@ public void setAutoDisplayPaused(boolean paused) {
164159
/**
165160
* Ask the SDK to re-evaluate whether a pending in-app message can be displayed now.
166161
* <p>
167-
* Use this when display was deferred via {@link IterableInAppDisplayHandler} (or
168-
* {@link #setAutoDisplayPaused(boolean)}) and your app has since become ready to show in-apps —
169-
* for example once a splash screen is dismissed and the main UI is visible. The SDK only
170-
* re-checks pending messages on its own triggers (foreground, sync, new message); calling this
171-
* lets the app prompt a re-check without one of those occurring. This does not change any stored
172-
* state; it triggers a single display attempt.
162+
* Use this when display was deferred — by returning {@link IterableInAppHandler.InAppResponse#DEFER}
163+
* from {@link IterableInAppHandler#onNewInApp(IterableInAppMessage)}, or via
164+
* {@link #setAutoDisplayPaused(boolean)} — and your app has since become ready to show in-apps,
165+
* for example once a splash screen is dismissed and the main UI is visible. The SDK otherwise
166+
* only re-checks pending messages on its own triggers (foreground, sync, new message); calling
167+
* this prompts a re-check without one of those occurring. This does not change any stored state;
168+
* it triggers a single display attempt.
173169
*/
174170
public void resumeInAppDisplay() {
175171
scheduleProcessing();
@@ -413,7 +409,7 @@ public int compare(IterableInAppMessage message1, IterableInAppMessage message2)
413409
}
414410

415411
private void processMessages() {
416-
if (!activityMonitor.isInForeground() || isShowingInApp() || !canShowInAppAfterPrevious()) {
412+
if (!activityMonitor.isInForeground() || isShowingInApp() || !canShowInAppAfterPrevious() || isAutoDisplayPaused()) {
417413
return;
418414
}
419415

@@ -424,12 +420,15 @@ private void processMessages() {
424420

425421
for (IterableInAppMessage message : messagesByPriorityLevel) {
426422
if (!message.isProcessed() && !message.isConsumed() && message.getTriggerType() == TriggerType.IMMEDIATE && !message.isRead()) {
427-
if (isAutoDisplayPaused(message)) {
428-
return;
429-
}
430423
IterableLogger.d(TAG, "Calling onNewInApp on " + message.getMessageId());
431424
InAppResponse response = handler.onNewInApp(message);
432425
IterableLogger.d(TAG, "Response: " + response);
426+
427+
if (response == InAppResponse.DEFER) {
428+
// Leave the message unprocessed so it is reconsidered on a later display pass.
429+
continue;
430+
}
431+
433432
message.setProcessed(true);
434433

435434
if (message.isJsonOnly()) {
@@ -480,8 +479,8 @@ private boolean canShowInAppAfterPrevious() {
480479
return getSecondsSinceLastInApp() >= inAppDisplayInterval;
481480
}
482481

483-
private boolean isAutoDisplayPaused(IterableInAppMessage message) {
484-
return displayHandler != null ? displayHandler.isAutoDisplayPaused(message) : autoDisplayPaused;
482+
boolean isAutoDisplayPaused() {
483+
return autoDisplayPaused;
485484
}
486485

487486
private void handleIterableCustomAction(String actionName, IterableInAppMessage message) {

iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class IterableInAppManagerSyncTest extends BaseTest {
3838
@Before
3939
public void setUp() throws Exception {
4040
MockitoAnnotations.initMocks(this);
41-
inAppManager = spy(new IterableInAppManager(iterableApiMock, handlerMock, null, 30.0, storageMock, activityMonitorMock, inAppDisplayerMock));
41+
inAppManager = spy(new IterableInAppManager(iterableApiMock, handlerMock, 30.0, storageMock, activityMonitorMock, inAppDisplayerMock));
4242
doAnswer(new Answer() {
4343
@Override
4444
public Object answer(InvocationOnMock invocation) throws Throwable {

0 commit comments

Comments
 (0)