diff --git a/CHANGELOG.md b/CHANGELOG.md
index a29097a2b..3b56c84d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,8 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- `IterableConfig.Builder.setExpiringAuthTokenRefreshPeriod(double)` accepts fractional seconds, matching the iOS, React Native and Flutter SDKs. Previously Android only accepted whole seconds, so a value like `0.5` behaved differently here than on other platforms. The existing `Long` overload is deprecated but still works, so no code changes are required.
+- Added `IterableConfig.Builder.setInAppColorScheme()` and `setInAppColorSchemeProvider()` to control the color scheme reported to HTML in-app messages. The provider is evaluated for each new in-app message, allowing apps that keep their theme in Jetpack Compose state to return the current `LIGHT` or `DARK` scheme.
### Fixed
+- HTML in-app messages now follow the host activity's Android light-dark configuration instead of always rendering as if the device were in dark mode. Previously the in-app container always reported `prefers-color-scheme: dark` to the message HTML, so a campaign with an `@media (prefers-color-scheme: dark)` block rendered its dark styles even in light mode. Those campaigns now render their light styles in light mode; campaigns that don't declare dark styles, and rendering in dark mode, are unaffected.
- Fixed the keychain treating a transient crypto timeout as a permanent decryption failure. A slow AndroidKeyStore operation that exceeded the 500 ms timeout would wipe the stored email, userId, and auth token and disable encryption, forcing the user to re-authenticate (and request a new auth token) on the next launch. Crypto timeouts are now handled as transient without wiping credentials or disabling encryption for the device: a read that times out returns no value for that call (the stored ciphertext is left intact for the next attempt), and a write that times out stores that one value unencrypted (as the non-encrypted fallback already did) rather than clearing everything. The timed-out crypto operation is also cancelled so it no longer blocks subsequent reads/writes.
- `setExpiringAuthTokenRefreshPeriod` now validates its input instead of silently producing a broken refresh schedule. Previously a negative value was converted to a negative millisecond period and then *subtracted* when computing the refresh time, scheduling the refresh after the token had already expired; a very large value overflowed to a negative period with the same effect; and `null` threw a `NullPointerException` on unboxing. Invalid values (`null`, `NaN`, negatives) are now logged and ignored, leaving the period at whatever it was before the call — the 60 second default unless an earlier call set something else. Values above ~10 years are clamped to that ceiling rather than ignored. Zero remains valid and means the token is refreshed only once it has expired.
- Fixed a `NullPointerException` in `EmbeddedSessionManager.updateDisplayCountAndDuration()` that could crash apps calling embedded session methods off the main thread. `EmbeddedSessionManager` is now internally synchronized, which also fixes concurrent modification of its impression map and duplicate session tracking when `endSession()` raced with itself. Thanks to [@Shamyyoun](https://github.com/Shamyyoun) for the report and initial fix.
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java
index a7c532d0b..710d7c5a7 100644
--- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableConfig.java
@@ -156,6 +156,17 @@ public class IterableConfig {
*/
final IterableInAppDisplayMode inAppDisplayMode;
+ /**
+ * Controls the color scheme reported to HTML in-app messages.
+ */
+ final IterableInAppColorScheme inAppColorScheme;
+
+ /**
+ * Supplies the current in-app color scheme when an in-app message is created.
+ */
+ @Nullable
+ final IterableInAppColorSchemeProvider inAppColorSchemeProvider;
+
/**
* Base URL for Webview content loading. Specifically used to enable CORS for external resources.
* If null or empty, defaults to empty string (original behavior with about:blank origin).
@@ -200,6 +211,8 @@ private IterableConfig(Builder builder) {
mobileFrameworkInfo = builder.mobileFrameworkInfo;
webViewBaseUrl = builder.webViewBaseUrl;
inAppDisplayMode = builder.inAppDisplayMode;
+ inAppColorScheme = builder.inAppColorScheme;
+ inAppColorSchemeProvider = builder.inAppColorSchemeProvider;
}
public static class Builder {
@@ -229,6 +242,8 @@ public static class Builder {
private IterableUnknownUserHandler iterableUnknownUserHandler;
private String webViewBaseUrl;
private IterableInAppDisplayMode inAppDisplayMode = IterableInAppDisplayMode.FORCE_EDGE_TO_EDGE;
+ private IterableInAppColorScheme inAppColorScheme = IterableInAppColorScheme.AUTOMATIC;
+ private IterableInAppColorSchemeProvider inAppColorSchemeProvider;
public Builder() {}
@@ -530,6 +545,36 @@ public Builder setInAppDisplayMode(@NonNull IterableInAppDisplayMode inAppDispla
return this;
}
+ /**
+ * Set the color scheme reported to HTML in-app messages. Defaults to
+ * {@link IterableInAppColorScheme#AUTOMATIC}, which follows the host activity's
+ * Android UI mode. Setting a fixed value clears any color scheme provider.
+ *
+ * @param inAppColorScheme the color scheme for HTML in-app messages
+ */
+ @NonNull
+ public Builder setInAppColorScheme(@NonNull IterableInAppColorScheme inAppColorScheme) {
+ this.inAppColorScheme = inAppColorScheme;
+ this.inAppColorSchemeProvider = null;
+ return this;
+ }
+
+ /**
+ * Set a provider that supplies the current color scheme whenever an HTML in-app
+ * message is created. Use this when the app's theme is held outside Android's
+ * configuration, such as theme state managed in Jetpack Compose. Setting a provider
+ * replaces any fixed color scheme.
+ *
+ * @param provider the provider for the current in-app color scheme
+ */
+ @NonNull
+ public Builder setInAppColorSchemeProvider(
+ @NonNull IterableInAppColorSchemeProvider provider) {
+ this.inAppColorScheme = IterableInAppColorScheme.AUTOMATIC;
+ this.inAppColorSchemeProvider = provider;
+ return this;
+ }
+
/**
* Set the base URL for WebView content loading. Used to enable CORS for external resources.
* If not set or null, defaults to empty string (original behavior with about:blank origin).
@@ -548,4 +593,4 @@ public IterableConfig build() {
}
}
-}
\ No newline at end of file
+}
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorScheme.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorScheme.java
new file mode 100644
index 000000000..0b8cda640
--- /dev/null
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorScheme.java
@@ -0,0 +1,21 @@
+package com.iterable.iterableapi;
+
+/**
+ * Controls the color scheme reported to HTML in-app messages.
+ */
+public enum IterableInAppColorScheme {
+ /**
+ * Follow the host activity's Android UI mode.
+ */
+ AUTOMATIC,
+
+ /**
+ * Report a light color scheme to in-app message HTML.
+ */
+ LIGHT,
+
+ /**
+ * Report a dark color scheme to in-app message HTML.
+ */
+ DARK
+}
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeProvider.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeProvider.java
new file mode 100644
index 000000000..364d2802c
--- /dev/null
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeProvider.java
@@ -0,0 +1,17 @@
+package com.iterable.iterableapi;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the color scheme to use when an HTML in-app message is created.
+ */
+@FunctionalInterface
+public interface IterableInAppColorSchemeProvider {
+ /**
+ * Returns the current color scheme for HTML in-app messages.
+ *
+ * @return the color scheme to use
+ */
+ @NonNull
+ IterableInAppColorScheme getInAppColorScheme();
+}
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeResolver.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeResolver.java
new file mode 100644
index 000000000..47d04d0a9
--- /dev/null
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppColorSchemeResolver.java
@@ -0,0 +1,60 @@
+package com.iterable.iterableapi;
+
+import androidx.annotation.NonNull;
+
+final class IterableInAppColorSchemeResolver {
+ private static final String TAG = "IterableInAppColorScheme";
+
+ private IterableInAppColorSchemeResolver() {
+ }
+
+ @NonNull
+ static IterableInAppColorScheme resolve() {
+ IterableConfig config = IterableApi.sharedInstance == null
+ ? null
+ : IterableApi.sharedInstance.config;
+ if (config == null) {
+ return IterableInAppColorScheme.AUTOMATIC;
+ }
+
+ IterableInAppColorSchemeProvider provider = config.inAppColorSchemeProvider;
+ if (provider == null) {
+ return config.inAppColorScheme;
+ }
+
+ try {
+ IterableInAppColorScheme colorScheme = provider.getInAppColorScheme();
+ if (colorScheme != null) {
+ return colorScheme;
+ }
+ IterableLogger.e(TAG, "Color scheme provider returned null; using AUTOMATIC");
+ } catch (Exception e) {
+ IterableLogger.e(TAG, "Color scheme provider failed; using AUTOMATIC", e);
+ }
+ return IterableInAppColorScheme.AUTOMATIC;
+ }
+
+ static int resolveDialogTheme() {
+ switch (resolve()) {
+ case LIGHT:
+ return R.style.Iterable_InAppDialog_Light;
+ case DARK:
+ return R.style.Iterable_InAppDialog_Dark;
+ case AUTOMATIC:
+ default:
+ return R.style.Iterable_InAppDialog;
+ }
+ }
+
+ static int resolveFragmentTheme() {
+ switch (resolve()) {
+ case LIGHT:
+ return R.style.Iterable_InAppFragment_Light;
+ case DARK:
+ return R.style.Iterable_InAppFragment_Dark;
+ case AUTOMATIC:
+ default:
+ return R.style.Iterable_InAppFragment;
+ }
+ }
+}
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppDialogNotification.kt b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppDialogNotification.kt
index cd14982bc..265d25d94 100644
--- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppDialogNotification.kt
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppDialogNotification.kt
@@ -47,7 +47,10 @@ class IterableInAppDialogNotification internal constructor(
private val webViewService: InAppWebViewService = InAppServices.webView,
private val orientationService: InAppOrientationService = InAppServices.orientation,
private val displayModeService: InAppDisplayModeService = InAppServices.displayMode
-) : Dialog(hostActivity, R.style.Iterable_InAppDialog), IterableWebView.HTMLNotificationCallbacks {
+) : Dialog(
+ hostActivity,
+ IterableInAppColorSchemeResolver.resolveDialogTheme()
+), IterableWebView.HTMLNotificationCallbacks {
private var webView: IterableWebView? = null
private var loaded: Boolean = false
@@ -522,4 +525,3 @@ class IterableInAppDialogNotification internal constructor(
trackingService.removeMessage(message)
}
}
-
diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
index df8aecc75..e484d3034 100644
--- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
+++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java
@@ -123,7 +123,9 @@ public IterableInAppFragmentHTMLNotification() {
this.backgroundAlpha = 0;
this.messageId = "";
insetPadding = new Rect();
- this.setStyle(DialogFragment.STYLE_NO_FRAME, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar);
+ this.setStyle(
+ DialogFragment.STYLE_NO_FRAME,
+ IterableInAppColorSchemeResolver.resolveFragmentTheme());
}
@Override
@@ -210,7 +212,9 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
return null;
}
- webView = createWebViewSafely(getContext());
+ // The WebView derives the CSS prefers-color-scheme value from its context theme's
+ // isLightTheme, so it needs the dialog's themed context and not the host activity's.
+ webView = createWebViewSafely(getDialog().getContext());
if (webView == null) {
dismissAllowingStateLoss();
return null;
diff --git a/iterableapi/src/main/res/values-v29/styles.xml b/iterableapi/src/main/res/values-v29/styles.xml
new file mode 100644
index 000000000..05fad6672
--- /dev/null
+++ b/iterableapi/src/main/res/values-v29/styles.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iterableapi/src/main/res/values/styles.xml b/iterableapi/src/main/res/values/styles.xml
index fbda81825..e0c6c801a 100644
--- a/iterableapi/src/main/res/values/styles.xml
+++ b/iterableapi/src/main/res/values/styles.xml
@@ -11,13 +11,15 @@
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableConfigTest.kt b/iterableapi/src/test/java/com/iterable/iterableapi/IterableConfigTest.kt
index 646105f1f..53e9d5eff 100644
--- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableConfigTest.kt
+++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableConfigTest.kt
@@ -36,6 +36,37 @@ class IterableConfigTest {
val config: IterableConfig = configBuilder.build()
assertThat(config.webViewBaseUrl, `is`("https://app.iterable.com"))
}
+
+ @Test
+ fun defaultInAppColorSchemeIsAutomatic() {
+ val config = IterableConfig.Builder().build()
+
+ assertEquals(IterableInAppColorScheme.AUTOMATIC, config.inAppColorScheme)
+ assertNull(config.inAppColorSchemeProvider)
+ }
+
+ @Test
+ fun setInAppColorSchemeUsesFixedValue() {
+ val config = IterableConfig.Builder()
+ .setInAppColorSchemeProvider { IterableInAppColorScheme.LIGHT }
+ .setInAppColorScheme(IterableInAppColorScheme.DARK)
+ .build()
+
+ assertEquals(IterableInAppColorScheme.DARK, config.inAppColorScheme)
+ assertNull(config.inAppColorSchemeProvider)
+ }
+
+ @Test
+ fun setInAppColorSchemeProviderUsesProvider() {
+ val provider = IterableInAppColorSchemeProvider { IterableInAppColorScheme.DARK }
+ val config = IterableConfig.Builder()
+ .setInAppColorScheme(IterableInAppColorScheme.LIGHT)
+ .setInAppColorSchemeProvider(provider)
+ .build()
+
+ assertEquals(IterableInAppColorScheme.AUTOMATIC, config.inAppColorScheme)
+ assertSame(provider, config.inAppColorSchemeProvider)
+ }
@Test
fun defaultDisableKeychainEncryption() {
diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppDialogNotificationTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppDialogNotificationTest.java
index b3431c10c..2d3bd20e3 100644
--- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppDialogNotificationTest.java
+++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppDialogNotificationTest.java
@@ -3,14 +3,18 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.graphics.Rect;
+import android.util.TypedValue;
import android.view.KeyEvent;
+import android.view.View;
import androidx.activity.ComponentActivity;
@@ -20,8 +24,11 @@
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowDialog;
+import java.util.concurrent.atomic.AtomicReference;
+
public class IterableInAppDialogNotificationTest extends BaseTest {
private ActivityController controller;
@@ -546,6 +553,96 @@ private IterableInAppDialogNotification createDialogWithTrackingServiceAndAttach
}
+ // ===== Light/Dark Theme Tests =====
+
+ @Test
+ public void dialogTheme_shouldResolveLight_inLightMode() {
+ IterableInAppDialogNotification dialog = createDialog();
+ dialog.show();
+
+ assertTrue("Dialog theme should resolve isLightTheme=true in light mode",
+ isLightTheme(dialog.getContext()));
+ }
+
+ @Test
+ @Config(qualifiers = "night")
+ public void dialogTheme_shouldResolveDark_inNightMode() {
+ IterableInAppDialogNotification dialog = createDialog();
+ dialog.show();
+
+ assertFalse("Dialog theme should resolve isLightTheme=false in night mode",
+ isLightTheme(dialog.getContext()));
+ }
+
+ @Test
+ public void dialogTheme_shouldUseExplicitDarkScheme_inLightMode() {
+ configureColorScheme(IterableInAppColorScheme.DARK);
+
+ IterableInAppDialogNotification dialog = createDialog();
+ dialog.show();
+
+ assertFalse("Explicit DARK should override the host activity's light mode",
+ isLightTheme(dialog.getContext()));
+ }
+
+ @Test
+ @Config(qualifiers = "night")
+ public void dialogTheme_shouldUseExplicitLightScheme_inNightMode() {
+ configureColorScheme(IterableInAppColorScheme.LIGHT);
+
+ IterableInAppDialogNotification dialog = createDialog();
+ dialog.show();
+
+ assertTrue("Explicit LIGHT should override the host activity's night mode",
+ isLightTheme(dialog.getContext()));
+ }
+
+ @Test
+ public void dialogTheme_shouldQueryProvider_forEachInApp() {
+ AtomicReference currentScheme =
+ new AtomicReference<>(IterableInAppColorScheme.DARK);
+ IterableTestUtils.resetIterableApi();
+ IterableTestUtils.createIterableApiNew(
+ builder -> builder.setInAppColorSchemeProvider(currentScheme::get));
+
+ IterableInAppDialogNotification darkDialog = createDialog();
+ darkDialog.show();
+ assertFalse("The first in-app should use the provider's DARK value",
+ isLightTheme(darkDialog.getContext()));
+ darkDialog.dismiss();
+
+ currentScheme.set(IterableInAppColorScheme.LIGHT);
+ IterableInAppDialogNotification lightDialog = createDialog();
+ lightDialog.show();
+ assertTrue("The next in-app should use the provider's updated LIGHT value",
+ isLightTheme(lightDialog.getContext()));
+ }
+
+ @Test
+ public void webView_shouldUseDialogThemedContext() {
+ IterableInAppDialogNotification dialog = createDialog();
+ dialog.show();
+
+ View webView = dialog.findViewById(R.id.webView);
+ assertNotNull(webView);
+ assertSame("WebView must be created with the dialog's themed context, otherwise it reports"
+ + " the host activity's prefers-color-scheme to the in-app HTML",
+ dialog.getContext(), webView.getContext());
+ }
+
+ private boolean isLightTheme(Context context) {
+ TypedValue value = new TypedValue();
+ assertTrue("isLightTheme should be resolvable on the in-app dialog theme",
+ context.getTheme().resolveAttribute(android.R.attr.isLightTheme, value, true));
+ return value.data != 0;
+ }
+
+ private void configureColorScheme(IterableInAppColorScheme colorScheme) {
+ IterableTestUtils.resetIterableApi();
+ IterableTestUtils.createIterableApiNew(
+ builder -> builder.setInAppColorScheme(colorScheme));
+ }
+
private IterableInAppDialogNotification createDialog() {
return createDialogWithPadding(new Rect(0, 0, 0, 0));
}
diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java
index 3d24d8cb4..46a2874a1 100644
--- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java
+++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppHTMLNotificationTest.java
@@ -1,7 +1,10 @@
package com.iterable.iterableapi;
+import android.content.Context;
import android.graphics.Rect;
+import android.util.TypedValue;
import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -13,13 +16,16 @@
import org.junit.Test;
import org.robolectric.Robolectric;
import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.shadows.ShadowLooper;
import static android.os.Looper.getMainLooper;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static org.robolectric.Shadows.shadowOf;
@@ -404,4 +410,78 @@ public void testRoundToNearest90Degrees_EdgeCases() {
assertEquals(360, IterableInAppFragmentHTMLNotification.roundToNearest90Degrees(315));
assertEquals(360, IterableInAppFragmentHTMLNotification.roundToNearest90Degrees(359));
}
+
+ // ===== Light/Dark Theme Tests =====
+
+ @Test
+ public void testDialogThemeFollowsLightMode() {
+ IterableInAppFragmentHTMLNotification notification = showNotification();
+
+ assertTrue("Dialog theme should resolve isLightTheme=true in light mode",
+ isLightTheme(notification.getDialog().getContext()));
+ }
+
+ @Test
+ @Config(qualifiers = "night")
+ public void testDialogThemeFollowsNightMode() {
+ IterableInAppFragmentHTMLNotification notification = showNotification();
+
+ assertFalse("Dialog theme should resolve isLightTheme=false in night mode",
+ isLightTheme(notification.getDialog().getContext()));
+ }
+
+ @Test
+ public void testDialogThemeUsesExplicitDarkSchemeInLightMode() {
+ configureColorScheme(IterableInAppColorScheme.DARK);
+
+ IterableInAppFragmentHTMLNotification notification = showNotification();
+
+ assertFalse("Explicit DARK should override the host activity's light mode",
+ isLightTheme(notification.getDialog().getContext()));
+ }
+
+ @Test
+ @Config(qualifiers = "night")
+ public void testDialogThemeUsesExplicitLightSchemeInNightMode() {
+ configureColorScheme(IterableInAppColorScheme.LIGHT);
+
+ IterableInAppFragmentHTMLNotification notification = showNotification();
+
+ assertTrue("Explicit LIGHT should override the host activity's night mode",
+ isLightTheme(notification.getDialog().getContext()));
+ }
+
+ @Test
+ public void testWebViewUsesDialogThemedContext() {
+ IterableInAppFragmentHTMLNotification notification = showNotification();
+
+ View webView = notification.getView().findViewById(R.id.webView);
+ assertNotNull(webView);
+ assertSame("WebView must be created with the dialog's themed context, otherwise it reports"
+ + " the host activity's prefers-color-scheme to the in-app HTML",
+ notification.getDialog().getContext(), webView.getContext());
+ }
+
+ private IterableInAppFragmentHTMLNotification showNotification() {
+ IterableInAppDisplayer.showIterableFragmentNotificationHTML(activity, "Test", "", null, 0.0, new Rect(), true, new IterableInAppMessage.InAppBgColor(null, 0.0f), false, IterableInAppLocation.IN_APP);
+ shadowOf(getMainLooper()).idle();
+
+ IterableInAppFragmentHTMLNotification notification = IterableInAppFragmentHTMLNotification.getInstance();
+ assertNotNull(notification);
+ assertNotNull(notification.getDialog());
+ return notification;
+ }
+
+ private void configureColorScheme(IterableInAppColorScheme colorScheme) {
+ IterableTestUtils.resetIterableApi();
+ IterableTestUtils.createIterableApiNew(
+ builder -> builder.setInAppColorScheme(colorScheme));
+ }
+
+ private boolean isLightTheme(Context context) {
+ TypedValue value = new TypedValue();
+ assertTrue("isLightTheme should be resolvable on the in-app dialog theme",
+ context.getTheme().resolveAttribute(android.R.attr.isLightTheme, value, true));
+ return value.data != 0;
+ }
}