diff --git a/.github/workflows/refresh-docs.yml b/.github/workflows/refresh-docs.yml index 6566458..181cf4b 100644 --- a/.github/workflows/refresh-docs.yml +++ b/.github/workflows/refresh-docs.yml @@ -111,6 +111,13 @@ jobs: ${{ steps.ref.outputs.sha }} "${{ steps.ref.outputs.label }}" + - name: Refresh snapshot + if: steps.changes.outputs.changed == 'true' + working-directory: pipeline + # polish:a wrote the fresh corpus to polished/; the snapshot must be + # regenerated from it or the PR fails its own snapshot:verify gate. + run: pnpm snapshot:refresh + - name: Open refresh PR if: steps.changes.outputs.changed == 'true' uses: peter-evans/create-pull-request@v6 diff --git a/.gitignore b/.gitignore index 402f4cf..10be05c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ proposals/ eval/prompts/ eval/results/ eval/report/ + +# JetBrains IDE files +.idea/ diff --git a/iterable-android/snapshot/android-app-links.md b/iterable-android/snapshot/android-app-links.md index 7a02923..0a9fac1 100644 --- a/iterable-android/snapshot/android-app-links.md +++ b/iterable-android/snapshot/android-app-links.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/deep-links/android-app-links/index.md source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3c933bbc8661bddfeaa8914f3dbb8233d98469c6 fetched_at: 2026-05-25T15:11:45.366Z -polished_at: 2026-06-05T14:02:08.697Z +polished_at: 2026-08-03T20:42:14.571Z layer: a snippets: - index: 0 diff --git a/iterable-android/snapshot/android-sdk.md b/iterable-android/snapshot/android-sdk.md index 3fb48cb..72d889b 100644 --- a/iterable-android/snapshot/android-sdk.md +++ b/iterable-android/snapshot/android-sdk.md @@ -8,10 +8,10 @@ title: Iterable's Android SDK source_url: https://support.iterable.com/hc/articles/360035019712 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/iterables-ios-and-android-sdks/android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: c0ebe8b736d334190bc4fcae820fa386642aa40d -fetched_at: 2026-05-25T15:11:39.936Z -polished_at: 2026-06-05T13:59:18.308Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: de67a132360a33146dae801ab62c3de1d6846ba9 +fetched_at: 2026-08-03T20:41:28.018Z +polished_at: 2026-08-03T20:42:14.561Z layer: a snippets: - index: 0 @@ -91,14 +91,46 @@ snippets: hash: 0450a5b49d68 line_count: 12 - index: 19 + lang: java + hash: e79d0e004450 + line_count: 4 + - index: 20 + lang: java + hash: acfa8af81cce + line_count: 5 + - index: 21 + lang: kotlin + hash: 18b42ba68e56 + line_count: 5 + - index: 22 + lang: java + hash: a16ba32ab078 + line_count: 3 + - index: 23 + lang: java + hash: 6f27d743e5d6 + line_count: 1 + - index: 24 + lang: java + hash: eaacdddcb017 + line_count: 1 + - index: 25 + lang: java + hash: 6b21444c769f + line_count: 5 + - index: 26 + lang: java + hash: 2f3eb2cbfb4e + line_count: 5 + - index: 27 lang: java hash: 1ee664bef567 line_count: 5 - - index: 20 + - index: 28 lang: java hash: 9edce984f563 line_count: 4 - - index: 21 + - index: 29 lang: groovy hash: 744057b87f30 line_count: 8 @@ -117,7 +149,7 @@ higher. ## Encrypted data Depending on your `minSdkVersion`, Iterable's Android SDK can encrypt some -data at rest. For more information, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +data at rest. For more information, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). ## Installing the SDK @@ -125,7 +157,7 @@ Follow these steps to install Iterable's Android SDK. If you're upgrading from a previous version, see [Upgrading the SDK](#upgrading-the-sdk). > [!WARNING] -> If your app targets API level 22 or lower, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10) +> If your app targets API level 22 or lower, read [Upgrading to 3.4.10](#upgrading-to-3-4-10) > to learn about some adjustments you'll need to make to your Android project. ### Step 1: Define a mobile app and push integration in Iterable @@ -309,7 +341,7 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` -For more information about this option, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +For more information about this option, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). #### Step 5.5: Specify a push integration name, if necessary @@ -504,10 +536,13 @@ as configured by your `RetryPolicy`, it stops attempting to refresh the JWT toke > > In addition to the `RetryPolicy` above (which controls JWT refresh scheduling), > the SDK supports automatic retry for offline-queued tasks that fail due to JWT -> expiration. When enabled via remote configuration, the offline task runner -> pauses authenticated tasks on a 401 error, refreshes the JWT, and retries +> expiration. When this feature is enabled, the offline task runner pauses +> authenticated tasks on a 401 error, refreshes the JWT, and retries > automatically. Unauthenticated API calls continue processing while > authentication is paused. This feature requires no code changes. +> +> This feature is not enabled by default. To turn it on for your project, ask +> your Iterable customer success manager to enable it for your account. It's also possible to _manually_ pause JWT token refresh attempts. To do this, call: @@ -657,7 +692,354 @@ To learn how to use Iterable's Android SDK with Embedded Messaging, read This section describes how to upgrade from earlier versions of Iterable's Android SDK. -### Upgrading to 3.5.12+ +### Upgrading to 3.10.0 + +[Version 3.10.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.10.0) +of Iterable's Android SDK makes manager getters fail gracefully before +initialization, and adds a `DEFER` response for in-app handlers, a +`resumeInAppDisplay()` method, and unknown user criteria fetch callbacks. +**No action is required to upgrade**—all of these changes are backward +compatible. + +#### Manager getters no longer crash before initialization + +In earlier versions, calling `getInAppManager()` or `getEmbeddedManager()` +before `IterableApi.initialize()` threw a `RuntimeException`, which could crash +the host app. Starting with version 3.10.0, these methods log an error and +return a no-op manager instead—it returns empty results and ignores commands, so +a call-ordering mistake no longer crashes your app. + +If you need to detect whether the SDK is initialized before using a manager, use +the new `getInAppManagerOrNull()` and `getEmbeddedManagerOrNull()` methods, which +return `null` (rather than a no-op manager) when the SDK isn't initialized yet. + +```java +IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManagerOrNull(); +if (inAppManager != null) { + // Safe to use; the SDK is initialized. +} +``` + +As always, initialize the SDK in the `onCreate` method of your `Application` +class before calling other SDK methods. + +#### New: `DEFER` response and `resumeInAppDisplay()` for in-app messages + +`IterableInAppHandler.InAppResponse` now includes a `DEFER` value. Unlike `SKIP` +(which permanently drops a message), `DEFER` keeps the message pending so the +SDK reconsiders it later—useful for temporary suppression, such as while a +splash screen is showing. To re-check pending messages on demand once your app +is ready, call the new `IterableInAppManager.resumeInAppDisplay()` method. For +more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231). + +#### New: unknown user criteria fetch callbacks + +`IterableUnknownUserHandler` now reports the results of unknown user criteria +fetches through two optional methods: `onCriteriaReceived(JSONObject criteria)` +on success and `onCriteriaFetchFailed(String reason)` on failure. Both have +default, no-op implementations, so existing handlers are unaffected. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.9.0 + +[Version 3.9.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.9.0) +of Iterable's Android SDK adds in-app message support for Jetpack Compose apps, +a new opt-in toolbar for the mobile inbox, and additional context for push-open +tracking. **No action is required to upgrade**—all of these changes are +backward compatible. + +#### In-app messages in Jetpack Compose apps + +The SDK can now render in-app messages using a new `Dialog`-based renderer +(`IterableInAppDialogNotification`) that doesn't require a `FragmentActivity`. +Apps that host in-app messages in a `FragmentActivity` continue to use the +existing `Fragment`-based rendering; apps that don't (such as those built fully +with Jetpack Compose, using a `ComponentActivity`) automatically fall back to +the `Dialog`-based renderer. As a result, in-app messages now display correctly +in apps built fully with Jetpack Compose, with no additional setup. + +#### New: `IterableInboxToolbarView` for the mobile inbox + +If you use Iterable's [Mobile Inbox](https://support.iterable.com/hc/articles/360038744152), +you can now add an optional toolbar above the inbox list using the new +`IterableInboxToolbarView`. Configure it with the `InboxToolbarOption` sealed +interface: + +- `None` (default) — No toolbar. The inbox behaves exactly as it did in + previous SDK versions. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To override it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. Views tagged with the + reserved IDs `@id/iterable_reserved_inbox_toolbar_action` and + `@id/iterable_reserved_inbox_toolbar_title` are automatically wired to the + SDK's back handler and title binding, respectively (both are optional). + +Configure the toolbar programmatically with `IterableInboxFragment.newInstance(...)` +(using the new two- or six-argument overloads), or with `IterableInboxActivity` +intent extras (`TOOLBAR_OPTION` and `TOOLBAR_TITLE`). + +> [!WARNING] +> When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +> descendant. + +For more information about customizing the inbox, see +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +#### New: `appAlreadyRunning` field on `trackPushOpen` + +`trackPushOpen` now includes an `appAlreadyRunning` field that indicates whether +the app was already running when the push notification was received. A new +`trackPushOpen(int, int, String, boolean, JSONObject)` overload lets you pass +this value; existing overloads default it to `false`, so no changes are required +for existing code. + +#### Fix: `TransactionTooLargeException` crash for large in-app messages + +This release also fixes a `TransactionTooLargeException` crash that could occur +when displaying in-app messages with oversized HTML payloads. The HTML is no +longer serialized into the fragment's saved instance state—it's reloaded from +storage when the fragment is recreated. In-app messages with missing HTML now +dismiss gracefully without registering tracking events, and a warning is logged +for HTML payloads that exceed the recommended size. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +### Upgrading to 3.8.0 + +[Version 3.8.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.8.0) +of Iterable's Android SDK introduces a new configuration option for controlling +how in-app messages interact with system bars, plus refinements to embedded +message views and a security cleanup. **No action is required for most apps**—upgrading +preserves the existing in-app message behavior introduced in 3.6.1. + +#### New: `IterableInAppDisplayMode` for in-app messages + +Since 3.6.1, Iterable's Android SDK has always rendered in-app messages +edge-to-edge, behind the status bar and navigation bar. Starting with 3.8.0, +you can change that behavior globally by setting an `IterableInAppDisplayMode` +on `IterableConfig`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FORCE_RESPECT_BOUNDS) + .build(); + +IterableApi.initialize(context, apiKey, config); +``` + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Draws in-app content behind the system + bars, with transparent status and navigation bars. Preserves the behavior + introduced in SDK 3.6.1. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current system bar + configuration. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never overlaps system bars, + keeping UI elements like the close button always accessible. + +If the close button on your fullscreen in-app messages is being obscured by +the status bar on certain devices, switch to `FOLLOW_APP_LAYOUT` or +`FORCE_RESPECT_BOUNDS`. For more information, see [Configuring how in-app messages interact with system bars](https://support.iterable.com/hc/articles/360035537231#configuring-how-in-app-messages-interact-with-system-bars-sdk-v3-8-0-and-above) +in the In-App Messages on Android documentation. + +#### Other changes in 3.8.0 + +- **`imageScaleType` option for embedded message views**: `IterableEmbeddedViewConfig` + exposes a new `imageScaleType` property that controls how the image is + scaled within the 16:9 container of an out-of-the-box embedded message view. + +- **Default values for `IterableEmbeddedViewConfig` parameters**: All + `IterableEmbeddedViewConfig` constructor parameters now have default values, + so you only need to specify the styling options you want to customize. + Existing calls that pass every parameter continue to work unchanged. + +- **Embedded message card layout fixes**: Out-of-the-box embedded message + views render correctly again on cards. The image now displays at a 16:9 + aspect ratio instead of collapsing to zero height, the card container no + longer expands to fill its parent, the missing end margin on the card is + applied, bottom spacing on buttons is no longer cut off, and the image is + properly clipped to the card's rounded corners. + +- **Removed insecure `AES/CBC/PKCS5Padding` encryption**: `IterableDataEncryptor` + now exclusively uses `AES/GCM/NoPadding`. The legacy CBC algorithm was only + used on Android versions below KitKat (API 19), which have been unsupported + since `minSdkVersion` was raised to 21 in SDK 3.5.12. No data migration is + required. + +### Upgrading to 3.7.0 + +[Version 3.7.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.7.0) +introduces two opt-in improvements: an automatic JWT-refresh-and-retry flow for the +offline event queue, and new callbacks for tracking embedded message sync results. +No application code changes are required to upgrade—both improvements are opt-in. + +#### Opt-in: Auto-retry for JWT failures in offline event processing + +When offline event processing is enabled and a queued API call returns a 401 +JWT error, the SDK can now automatically: + +1. Pause processing of authenticated tasks in the offline queue. +2. Refresh the JWT via your registered `IterableAuthHandler`. +3. Retry the failed task with the new token. + +Unauthenticated endpoints (such as `disableDevice`, `mergeUser`, and +`trackConsent`) continue to be processed while authentication is paused, so +unrelated traffic isn't blocked behind a stale token. + +This behavior is disabled by default for existing customers. To enable it for your +project, talk to your Iterable customer success manager. No application code +changes are required once the flag is enabled—the SDK starts using the new behavior +automatically. + +#### Opt-in: Embedded messaging sync callbacks + +`IterableEmbeddedUpdateHandler` now exposes two optional callbacks— +`onEmbeddedMessagingSyncSucceeded()` and `onEmbeddedMessagingSyncFailed(reason)`— +that let your app react to embedded message syncs. Use them to stop a loading +spinner on success or to show fallback content on failure. Both methods have +default empty implementations, so existing code keeps working unchanged. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652#step-8-set-up-sdk-listeners). + +### Upgrading to 3.6.6 + +[Version 3.6.6](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.6) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.5 + +Starting with [version 3.6.5](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.5), +the `IterableEmbeddedView` constructor is **deprecated** because it violates +Android Fragment best practices: the system can't recreate the fragment after +configuration changes or process death, which can cause crashes. + +Use the `newInstance` factory method instead: + +```kotlin +// Deprecated: +val messageView = IterableEmbeddedView(ootbType, message, config) + +// Use this instead: +val messageView = IterableEmbeddedView.newInstance(ootbType, message, config) +``` + +The old constructor still works, but it's marked as deprecated and will be +removed in a future SDK release. Update your application code now to avoid a +breaking change later. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652). + +### Upgrading to 3.6.4 + +[Version 3.6.4](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.4) +makes the `isIterableDeeplink` method public so you can now check whether a URL is +an Iterable deep link before handling it. The method returns `true` when the URL +matches the Iterable deep link pattern (URLs containing `/a/` in the path). + +`isIterableDeeplink` is a **static** method on `IterableApi`: + +```java +if (IterableApi.isIterableDeeplink(urlString)) { + // URL is an Iterable deep link +} +``` + +For more information about deep links in Iterable, read [Android App Links](https://support.iterable.com/hc/articles/360035127392). + +### Upgrading to 3.6.3 + +[Version 3.6.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.3) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.2 + +[Version 3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) +adds three opt-in capabilities. No action is required to upgrade. + +- **Background initialization to prevent ANRs**: To run SDK initialization on + a background thread (with API calls automatically queued until ready), call + the new `IterableApi.initializeInBackground()` static method instead of + `IterableApi.initialize()`: + + ```java + IterableApi.initializeInBackground(context, apiKey, config, callback); + ``` + + Use this if running initialization on the main thread is contributing to + Application Not Responding (ANR) errors during app startup. The optional + `callback` (an `IterableInitializationCallback`) is invoked when + initialization completes. + +- **`onSDKInitialized()` callback**: A new static method on `IterableApi` lets + you subscribe a callback to be notified when initialization completes. Use + it when you need to defer SDK-dependent work from multiple call sites—for + example, posting the first event only after the SDK is fully ready. + + ```java + IterableApi.onSDKInitialized(callback); + ``` + +- **`setWebViewBaseUrl()` configuration option**: A new `IterableConfig.Builder` + method that sets the base URL used by WebView-based messages (in-app + messages, inbox, and embedded messages). Set it when you self-host custom + fonts or other external resources that require CORS to load successfully in + a WebView: + + ```java + IterableConfig config = new IterableConfig.Builder() + .setWebViewBaseUrl("https://your-cdn.example.com") + .build(); + + IterableApi.initialize(context, apiKey, config); + ``` + + If not set, the base URL defaults to an empty string (the original behavior). + +### Upgrading to 3.6.1 + +Starting with [version 3.6.1](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.1), +in-app messages render edge-to-edge so they display properly on devices with notches, +cutouts, and system bars. + +By default, the SDK applies white insets to fill the area behind the system +bars. In dark-themed apps, that white can contrast sharply with your in-app +message content. + +If your app uses a dark theme, consider updating the [background overlay](https://support.iterable.com/hc/articles/360044425951#background-overlay) +on your in-app templates to a color that complements your app, and test +existing templates before publishing. + +### Upgrading to 3.6.0 + +To enable Unknown User Activation, upgrade to [version 3.6.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.0) +of Iterable's Android SDK and call `setEnableUnknownUserActivation(true)` on +`IterableConfig.Builder` before initializing the SDK. These code changes are +only required if you want to use Unknown User Activation; otherwise, no +changes are required. + +```java +IterableConfig config = new IterableConfig.Builder() + .setEnableUnknownUserActivation(true) + .build(); + +IterableApi.initialize(context, "", config); +``` + +The SDK also captures user consent on your behalf when this feature is enabled. For full +setup instructions, read [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.5.12 - **Supported Android versions**: Beginning with [version 3.5.12](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.12), Iterable's Android SDK supports Android versions 5.0 (API level 21) and @@ -675,7 +1057,7 @@ Android SDK. IterableApi.initialize(context, apiKey, config); ``` -### Upgrading to 3.5.3+ +### Upgrading to 3.5.3 Starting with [version 3.5.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.3), Iterable's Android SDK provides more insight into JWT refresh failures, to help @@ -691,9 +1073,9 @@ If you've implemented that method, you'll need to update your application code. For more information, see [Step 5.6.1: Register an auth handler](#step-5-6-1-register-an-auth-handler). -### Upgrading to 3.5.2+ +### Upgrading to 3.5.2 -When upgrading to [version 3.5.2+](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) +When upgrading to [version 3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) of the SDK, you can make use of the `setAuthRetryPolicy` method on `IterableConfig` to specify: @@ -702,7 +1084,7 @@ to specify: - The interval between each retry attempt. Defaults to 6 seconds. - A backoff strategy: linear or exponential. Defaults to linear. -### Upgrading to 3.4.10+ +### Upgrading to 3.4.10 In Android apps with `minSdkVersion` 23 or higher ([Android 6.0](https://developer.android.com/studio/releases/platforms#6.0)) Iterable's Android SDK now encrypts the following fields when storing them at @@ -753,9 +1135,9 @@ encrypt data): - Add `multiDexEnabled true` to the `default` object, under `android`. - Add `implementation androidx.multidex:multidex:2.0.1` to the `dependencies`. -### Upgrading to 3.4.0+ +### Upgrading to 3.4.0 -- Starting with version [`3.4.0`](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) +- Starting with version [3.4.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) of Iterable's Android SDK, you'll need to declare the URL protocols that the SDK should expect to see on incoming links (and then handle as needed). For more information, read about [Step 5.3: Set allowed URL protocols](#step-5-3-set-allowed-url-protocols), @@ -766,7 +1148,7 @@ encrypt data): need to first grab an instance of the `IterableApi` class by calling `IterableApi.getInstance()`. For example, `IterableApi.getInstance().handleAppLink(...)`. -### Upgrading to 3.3.1+ +### Upgrading to 3.3.1 To resolve a breaking change introduced in Firebase Cloud Messaging [version 22.0.0](https://firebase.google.com/support/release-notes/android#messaging_v22-0-0), @@ -788,9 +1170,9 @@ android { } ``` -### Upgrading to 3.2.0+ +### Upgrading to 3.2.0 -[Versions 3.2.0+ of the SDK](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) +[Versions 3.2.0 and higher](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) depend on the [AndroidX](https://developer.android.com/jetpack/androidx) support libraries. To use these versions, you'll need to [migrate your app to use AndroidX](https://developer.android.com/jetpack/androidx/migrate). diff --git a/iterable-android/snapshot/configure-the-android-sdk.md b/iterable-android/snapshot/configure-the-android-sdk.md index d946199..2908576 100644 --- a/iterable-android/snapshot/configure-the-android-sdk.md +++ b/iterable-android/snapshot/configure-the-android-sdk.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/unknown-user-activation-dev/configure-t source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: fa441fa69f35c816affd3df2d565dbfbd2727ca3 fetched_at: 2026-05-25T15:11:48.790Z -polished_at: 2026-06-05T13:59:18.319Z +polished_at: 2026-08-03T20:42:14.575Z layer: a snippets: - index: 0 diff --git a/iterable-android/snapshot/customizing-mobile-inbox-on-android.md b/iterable-android/snapshot/customizing-mobile-inbox-on-android.md index 3b08f11..62e83de 100644 --- a/iterable-android/snapshot/customizing-mobile-inbox-on-android.md +++ b/iterable-android/snapshot/customizing-mobile-inbox-on-android.md @@ -5,13 +5,13 @@ archetype: feature sdk_min_version: 3.7.0 sdk_artifact: iterableapi title: Customizing Mobile Inbox on Android -source_url: https://iterable.zendesk.com/hc/articles/360039189931 +source_url: https://support.iterable.com/hc/articles/360039189931 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/customizing-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 502be69e2212a0540bcaaad80299d4e3936bb656 -fetched_at: 2026-05-25T15:11:42.777Z -polished_at: 2026-06-05T13:59:18.313Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: e156e9f1bf13e4b41507a53dc10093422c8aefac +fetched_at: 2026-08-03T20:41:29.489Z +polished_at: 2026-08-03T20:42:14.567Z layer: a snippets: - index: 0 @@ -35,42 +35,58 @@ snippets: hash: 00a988c5c250 line_count: 3 - index: 5 + lang: kotlin + hash: d7b7d23c4c48 + line_count: 4 + - index: 6 + lang: java + hash: d46242e73662 + line_count: 4 + - index: 7 + lang: kotlin + hash: 689b12ae918a + line_count: 4 + - index: 8 + lang: java + hash: 584a986d1076 + line_count: 4 + - index: 9 lang: kotlin hash: edcae1e6e4de line_count: 13 - - index: 6 + - index: 10 lang: java hash: ef4f78408c7b line_count: 18 - - index: 7 + - index: 11 lang: kotlin hash: d121dfcd6053 line_count: 6 - - index: 8 + - index: 12 lang: kotlin hash: 6bb02c0de57c line_count: 10 - - index: 9 + - index: 13 lang: java hash: 80dd3262733b line_count: 13 - - index: 10 + - index: 14 lang: kotlin hash: 3cbc544d3efa line_count: 8 - - index: 11 + - index: 15 lang: kotlin hash: 8a0fe4b9b840 line_count: 10 - - index: 12 + - index: 16 lang: java hash: 8f7a8b76a82d line_count: 13 - - index: 13 + - index: 17 lang: kotlin hash: a28bb9940c6d line_count: 48 - - index: 14 + - index: 18 lang: java hash: 1ff85a5943c1 line_count: 53 @@ -157,7 +173,7 @@ default) or as standalone activities. To change this setting, either: - Set an extra for the activity's intent: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -165,7 +181,7 @@ default) or as standalone activities. To change this setting, either: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -175,13 +191,13 @@ default) or as standalone activities. To change this setting, either: - Pass constructor parameters to the fragment: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY, 0) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY) @@ -192,7 +208,7 @@ default) or as standalone activities. To change this setting, either: When launching the mobile inbox as an activity, change the title by passing an `activityTitle` argument in the intent: -Kotlin: +_Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -200,7 +216,7 @@ intent.putExtra("activityTitle", "My Inbox") startActivity(intent) ``` -Java: +_Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -208,7 +224,79 @@ intent.putExtra("activityTitle", "My Inbox"); startActivity(intent); ``` -### Cell layout, colors and font +### Inbox toolbar (SDK v3.9.0 and above) + +Starting with SDK version 3.9.0, you can display an optional toolbar above the +inbox list using `IterableInboxToolbarView`. The toolbar is off by default, +so the inbox behaves exactly as it did in previous SDK versions unless you opt +in. + +Configure the toolbar with the `InboxToolbarOption` sealed interface, which has +these options: + +- `None` (default) — No toolbar. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To customize it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. To wire your layout to + the SDK, tag views with these reserved IDs (both are optional): + - `@id/iterable_reserved_inbox_toolbar_action` — Automatically wired to the + SDK's back handler. + - `@id/iterable_reserved_inbox_toolbar_title` — Automatically bound to the + toolbar title. + +> [!WARNING] +> When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +> descendant. + +#### Configure the toolbar on the fragment + +Pass an `InboxToolbarOption` (and, optionally, a title) to +`IterableInboxFragment.newInstance(...)`: + +_Kotlin_ + +```kotlin +val inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton, + "My Inbox" +) +``` + +_Java_ + +```java +IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton.INSTANCE, + "My Inbox" +); +``` + +#### Configure the toolbar on the activity + +When launching the inbox as an activity, set the `TOOLBAR_OPTION` and +`TOOLBAR_TITLE` intent extras: + +_Kotlin_ + +```kotlin +val intent = Intent(context, IterableInboxActivity::class.java) +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton) +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox") +startActivity(intent) +``` + +_Java_ + +```java +Intent intent = new Intent(getContext(), IterableInboxActivity.class); +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton.INSTANCE); +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox"); +startActivity(intent); +``` + +### Cell layout, colors, and font > [!TIP] > In the [sample app](#sample-app), tap **Inbox with Custom Cell** to see an @@ -225,7 +313,7 @@ To modify the font, color or layout of inbox cells: 3. Specify this layout ID when launching the activity: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -233,7 +321,7 @@ To modify the font, color or layout of inbox cells: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -243,13 +331,13 @@ To modify the font, color or layout of inbox cells: 4. Alternatively, create the fragment with custom parameters: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item); @@ -266,7 +354,7 @@ subclass `IterableInboxFragment` and set a date mapper in `onCreate`. The date mapper takes an `IterableInAppMessage` and returns a string representing the creation date of the message. If the date field should be blank, return `null`. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxDateMapperFragment : IterableInboxFragment() { @@ -284,7 +372,7 @@ class CustomInboxDateMapperFragment : IterableInboxFragment() { } ``` -Java: +_Java_ ```java public class CustomInboxDateMapperJavaFragment extends IterableInboxFragment implements IterableInboxDateMapper { @@ -321,7 +409,7 @@ the message, `false` otherwise. `IterableInboxFilter` is an interface that declares a filter method. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFilterFragment : IterableInboxFragment() { @@ -347,7 +435,7 @@ class CustomInboxFilterFragment : IterableInboxFragment(), IterableInboxFilter { } ``` -Java: +_Java_ ```java public class CustomInboxFilterFragment extends IterableInboxFragment implements IterableInboxFilter { @@ -380,7 +468,7 @@ set a comparator in `onCreate`. `IterableInboxComparator` is a standard Java `Comparator` interface: return a negative integer, zero, or a positive integer when the first message is less than, equal to, or greater than the second. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxComparatorFragment : IterableInboxFragment() { @@ -480,7 +568,7 @@ To display different inbox items with different interfaces, follow these steps: 5. The same constants will then be passed to `getLayoutForViewType`. Use them to return different layouts based on the view type. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterExtension { @@ -533,7 +621,7 @@ class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterE } ``` -Java: +_Java_ ```java public class CustomInboxFieldsJavaFragment extends IterableInboxFragment implements IterableInboxAdapterExtension { diff --git a/iterable-android/snapshot/deep-linking-with-partners.md b/iterable-android/snapshot/deep-linking-with-partners.md index 5b3506d..6129dbe 100644 --- a/iterable-android/snapshot/deep-linking-with-partners.md +++ b/iterable-android/snapshot/deep-linking-with-partners.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/deep-links/deep-linking-with-partners/i source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 7473a924f2b7eac5a08f7ec66c3fbf60d07089e4 fetched_at: 2026-05-25T15:11:46.035Z -polished_at: 2026-06-05T13:59:18.317Z +polished_at: 2026-08-03T20:42:14.572Z layer: a snippets: [] summary: "Iterable supports deep linking without any third-party diff --git a/iterable-android/snapshot/embedded-messages-with-iterables-android-sdk.md b/iterable-android/snapshot/embedded-messages-with-iterables-android-sdk.md index 0da103a..4c67603 100644 --- a/iterable-android/snapshot/embedded-messages-with-iterables-android-sdk.md +++ b/iterable-android/snapshot/embedded-messages-with-iterables-android-sdk.md @@ -8,10 +8,10 @@ title: Embedded Messages with Iterable's Android SDK source_url: https://support.iterable.com/hc/articles/23061877893652 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/embedded-messaging/embedded-messages-with-iterables-android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 6a6d6b5baf76973d611fb7f17785d3b04feeace0 -fetched_at: 2026-05-25T15:11:44.823Z -polished_at: 2026-06-05T13:59:18.315Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 576150056520b366d5190411e9f28198e70bbeaf +fetched_at: 2026-08-03T20:41:31.068Z +polished_at: 2026-08-03T20:42:14.570Z layer: a snippets: - index: 0 @@ -52,41 +52,45 @@ snippets: line_count: 23 - index: 9 lang: kotlin - hash: b21729c5cb5d - line_count: 13 + hash: b259e6a45e17 + line_count: 14 - index: 10 + lang: kotlin + hash: a2932531b4f0 + line_count: 4 + - index: 11 lang: kotlin hash: 38e520b049c7 line_count: 1 - - index: 11 + - index: 12 lang: kotlin hash: 48c85c7cb874 line_count: 2 - - index: 12 + - index: 13 lang: xml hash: d9b27fbb15f5 line_count: 12 - - index: 13 + - index: 14 lang: kotlin hash: 3887b4569179 line_count: 3 - - index: 14 + - index: 15 lang: kotlin hash: b64c0dd160ee line_count: 7 - - index: 15 + - index: 16 lang: kotlin hash: a8b0449c17f6 line_count: 13 - - index: 16 + - index: 17 lang: kotlin hash: 97d5fb776fc9 line_count: 7 - - index: 17 + - index: 18 lang: kotlin hash: 1048049cb459 line_count: 8 - - index: 18 + - index: 19 lang: kotlin hash: 820e05f6b034 line_count: 7 @@ -529,19 +533,45 @@ to declare the styles you'd like the view to use: ```kotlin // Grab your app's colors from wherever it makes sense. val config = IterableEmbeddedViewConfig( - backgroundColor: Color.parseColor("#FFFFFF"), - borderColor: Color.parseColor("#000000"), - borderWidth: 1, - borderCornerRadius: 8f, - primaryBtnBackgroundColor: Color.parseColor("#0000FF"), - primaryBtnTextColor: Color.parseColor("#FFFFFF"), - secondaryBtnBackgroundColor: Color.parseColor("#FFFFFF"), - secondaryBtnTextColor: Color.parseColor("#000000"), - titleTextColor: Color.parseColor("#000000"), - bodyTextColor: Color.parseColor("#000000") + backgroundColor = Color.parseColor("#FFFFFF"), + borderColor = Color.parseColor("#000000"), + borderWidth = 1, + borderCornerRadius = 8f, + primaryBtnBackgroundColor = Color.parseColor("#0000FF"), + primaryBtnTextColor = Color.parseColor("#FFFFFF"), + secondaryBtnBackgroundColor = Color.parseColor("#FFFFFF"), + secondaryBtnTextColor = Color.parseColor("#000000"), + titleTextColor = Color.parseColor("#000000"), + bodyTextColor = Color.parseColor("#000000"), + imageScaleType = ImageView.ScaleType.CENTER_CROP +) +``` + +**💡 TIP — Default values (SDK v3.8.0 and above)** + +Starting with SDK version 3.8.0, all `IterableEmbeddedViewConfig` parameters +have default values, so you only need to specify the styling options you want +to customize. The example above shows every option for reference, but you can pass +just the ones you need. For example: + +```kotlin +val config = IterableEmbeddedViewConfig( + backgroundColor = Color.parseColor("#FFFFFF"), + borderCornerRadius = 8f ) ``` +All color, border, and text-color parameters default to `null` (which falls +back to the view's built-in styling). The `imageScaleType` parameter defaults +to `ImageView.ScaleType.CENTER_CROP`. + +The `imageScaleType` parameter (added in SDK v3.8.0) controls how the image is +scaled within the 16:9 image container of `CARD` and `BANNER` views. It accepts +any standard Android [`ImageView.ScaleType`](https://developer.android.com/reference/android/widget/ImageView.ScaleType) +value (for example, `CENTER_CROP`, `FIT_CENTER`, or `FIT_XY`). The +`NOTIFICATION` view type does not display an image, so this parameter has no +effect on that view type. + Then, when it's time to display a message, create the `IterableEmbeddedView` using the `newInstance` factory method: diff --git a/iterable-android/snapshot/identifying-the-user.md b/iterable-android/snapshot/identifying-the-user.md index 22cbf0b..429f386 100644 --- a/iterable-android/snapshot/identifying-the-user.md +++ b/iterable-android/snapshot/identifying-the-user.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/managing-user-profiles/identifying-the- source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: ced31ca29ce63d634a0c4691277a114ed3f0ceb9 fetched_at: 2026-05-25T15:11:46.888Z -polished_at: 2026-06-05T13:59:18.317Z +polished_at: 2026-08-03T20:42:14.572Z layer: a snippets: - index: 0 diff --git a/iterable-android/snapshot/in-app-messages-on-android.md b/iterable-android/snapshot/in-app-messages-on-android.md index e75489a..1e6edba 100644 --- a/iterable-android/snapshot/in-app-messages-on-android.md +++ b/iterable-android/snapshot/in-app-messages-on-android.md @@ -8,10 +8,10 @@ title: In-App Messages on Android source_url: https://support.iterable.com/hc/articles/360035537231 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/in-app-messages-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 67ade49d8716b4d65cdb7779eb210afa0b5054da -fetched_at: 2026-05-27T12:44:52.898Z -polished_at: 2026-06-05T13:59:18.311Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 65412ae773eaca59531243ff4775fd4457b5b608 +fetched_at: 2026-08-03T20:41:28.539Z +polished_at: 2026-08-03T20:42:14.565Z layer: a snippets: - index: 0 @@ -19,17 +19,33 @@ snippets: hash: 8db83c22f9a3 line_count: 18 - index: 1 + lang: java + hash: 41e8696aa8cc + line_count: 9 + - index: 2 lang: java hash: bcfe488ba3d1 line_count: 10 - - index: 2 + - index: 3 + lang: java + hash: a903f600bb27 + line_count: 4 + - index: 4 lang: kotlin hash: 28725ee7538d line_count: 1 - - index: 3 + - index: 5 lang: java hash: 265c585feb80 line_count: 1 + - index: 6 + lang: kotlin + hash: 34caae496955 + line_count: 1 + - index: 7 + lang: java + hash: e4f4f768e9bb + line_count: 1 summary: By default, when an in-app message arrives from the server, the SDK automatically shows it if the app is in the foreground. If an in-app message is already showing when the new message arrives, the new message will be shown @@ -83,6 +99,36 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` +### Deferring an in-app message (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, `onNewInApp` can also return +`InAppResponse.DEFER`. Unlike `SKIP`, which permanently drops the message, +`DEFER` keeps the message pending so the SDK reconsiders it on a later display +pass (for example, on the next foreground, sync, or newly arrived message). This +is useful for temporary, per-message suppression—for example, while a splash +screen is showing. + +```java +class MyInAppHandler implements IterableInAppHandler { + @Override + public InAppResponse onNewInApp(IterableInAppMessage message) { + if (appIsShowingSplashScreen()) { + return InAppResponse.DEFER; + } + return InAppResponse.SHOW; + } +} +``` + +Once your app is ready to display in-app messages, call +`resumeInAppDisplay()` (see [Pausing the display of in-app messages](#pausing-the-display-of-in-app-messages-sdk-v3-2-6-and-above)) +to re-check pending messages immediately, instead of waiting for the next +foreground or sync trigger. + +> [!NOTE] +> In Kotlin, add a `DEFER` branch to any exhaustive `when` expression over +> `InAppResponse`. + ## Getting the local queue of in-app messages The SDK keeps the local in-app message queue in sync by checking the server queue @@ -131,6 +177,69 @@ The SDK handles in-app message buttons and links as follows: returns `false` for the provided URL, the URL will be opened by the system (using a web browser or other application, as applicable). +## Configuring how in-app messages interact with system bars (SDK v3.8.0 and above) + +By default, Iterable's Android SDK draws in-app messages edge-to-edge, with +content extending behind the status bar and navigation bar. This was the only +behavior in SDK versions 3.6.1 through 3.7.0. + +Starting with SDK version 3.8.0, you can configure how in-app messages interact +with system bars by setting `IterableInAppDisplayMode` on `IterableConfig`. +This setting applies globally to all in-app messages displayed by the SDK. + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Forces in-app messages to display + edge-to-edge, drawing content behind the status bar and navigation bar. + This preserves the behavior of previous SDK versions. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current layout configuration. + If your app is edge-to-edge, in-app messages display edge-to-edge; if your + app respects system bar bounds, so do in-app messages. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. Uses the legacy `FLAG_FULLSCREEN` on API levels below 30 and + `WindowInsetsController` on API 30 and above. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never draws behind the + status bar or navigation bar, keeping UI elements like the close button + always accessible. + +To configure the display mode, call `setInAppDisplayMode()` on +`IterableConfig.Builder`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FOLLOW_APP_LAYOUT) + .build(); +IterableApi.initialize(context, "", config); +``` + +> [!TIP] +> If the close button (or other interactive elements) in your fullscreen in-app +> messages is being obscured by the status bar, switch to `FOLLOW_APP_LAYOUT` +> or `FORCE_RESPECT_BOUNDS`. + +## Displaying in-app messages in Jetpack Compose apps (SDK v3.9.0 and above) + +In SDK versions before 3.9.0, displaying an in-app message required a +`FragmentActivity`, because the SDK rendered in-app messages using a `Fragment`. +This meant that apps built fully with [Jetpack Compose](https://developer.android.com/compose) +(and without the Android fragment framework) couldn't display in-app messages. + +Starting with SDK version 3.9.0, the SDK can also render in-app messages using a +`Dialog`-based renderer (`IterableInAppDialogNotification`) that doesn't require +a `FragmentActivity`. When the current activity is a `FragmentActivity`, the SDK +continues to use the existing `Fragment`-based rendering; when it isn't (for +example, a Compose-based `ComponentActivity`), the SDK falls back to the +`Dialog`-based renderer. As a result, in-app messages now display correctly in +apps built fully with Jetpack Compose. + +No code changes are required to take advantage of this—just upgrade to SDK +version 3.9.0 or later. (The host must still be an `Activity`.) + +> [!WARNING] +> This Compose compatibility applies to **in-app message rendering** only. Iterable's +> mobile inbox UI is still fragment-based and requires a `FragmentActivity` host. For +> more information, see [Setting up Mobile Inbox on Android](https://support.iterable.com/hc/articles/360038744152#displaying-the-mobile-inbox). + ## Changing the display interval between in-app messages To customize the time delay between successive in-app messages, set @@ -162,4 +271,30 @@ However, it will keep the local queue of in-app messages in sync. > method on `IterableInAppManager` to manually display messages. To resume the display of in-app messages from your app's queue, call -`setAutoDisplayPaused(false)`. +`setAutoDisplayPaused(false)`. + +### Re-evaluating pending in-app messages on demand (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, you can call `resumeInAppDisplay()` to prompt +the SDK to re-evaluate pending in-app messages once your app is ready to display +them—for example, after a splash screen is dismissed, or after you deferred a +message by returning `InAppResponse.DEFER` from `onNewInApp` (see +[Deferring an in-app message](#deferring-an-in-app-message-sdk-v3-10-0-and-above)). +Without this call, the SDK re-checks pending messages only on its own triggers +(foreground, sync, or a newly arrived message). + +_Kotlin_ + +```kotlin +IterableApi.getInstance().inAppManager.resumeInAppDisplay() +``` + +_Java_ + +```java +IterableApi.getInstance().getInAppManager().resumeInAppDisplay(); +``` + +`resumeInAppDisplay()` is independent of `setAutoDisplayPaused(boolean)`: if +automatic display is paused, this call won't show anything (and logs a warning) +until you also call `setAutoDisplayPaused(false)`. diff --git a/iterable-android/snapshot/push-notification-overview.md b/iterable-android/snapshot/push-notification-overview.md index 5354a2d..6397f72 100644 --- a/iterable-android/snapshot/push-notification-overview.md +++ b/iterable-android/snapshot/push-notification-overview.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/push-notifications/push-notification-ov source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3306f88835e0c1b30e4b4020287d772cfd93ba1e fetched_at: 2026-05-25T15:11:44.170Z -polished_at: 2026-06-05T13:59:18.315Z +polished_at: 2026-08-03T20:42:14.569Z layer: a snippets: [] summary: To alert users about updates, offers, content, and other information diff --git a/iterable-android/snapshot/setting-up-android-push-notifications.md b/iterable-android/snapshot/setting-up-android-push-notifications.md index 91da677..41f48f6 100644 --- a/iterable-android/snapshot/setting-up-android-push-notifications.md +++ b/iterable-android/snapshot/setting-up-android-push-notifications.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/push-notifications/setting-up-android-p source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 45fa32087746810e08046766184fd4c2eb1acc94 fetched_at: 2026-05-25T15:11:43.429Z -polished_at: 2026-06-05T13:59:18.314Z +polished_at: 2026-08-03T20:42:14.568Z layer: a snippets: - index: 0 diff --git a/iterable-android/snapshot/setting-up-mobile-inbox-on-android.md b/iterable-android/snapshot/setting-up-mobile-inbox-on-android.md index cf38abd..36a6e21 100644 --- a/iterable-android/snapshot/setting-up-mobile-inbox-on-android.md +++ b/iterable-android/snapshot/setting-up-mobile-inbox-on-android.md @@ -8,10 +8,10 @@ title: Setting up Mobile Inbox on Android source_url: https://support.iterable.com/hc/articles/360038744152 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/setting-up-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 968c6cc97c2aec2e8e4124b5ef18aa9a000a139d -fetched_at: 2026-05-25T15:11:41.984Z -polished_at: 2026-06-05T13:59:18.312Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 9b54bece973b76efd0e1eaec0494b0e9d2c2af7c +fetched_at: 2026-08-03T20:41:29.000Z +polished_at: 2026-08-03T20:42:14.566Z layer: a snippets: [] summary: Apps using version 3.2.0 and later of Iterable's [Android @@ -46,6 +46,20 @@ To add a mobile inbox to your Android app, first install Iterable's ## Displaying the mobile inbox +> [!WARNING] +> Iterable's mobile inbox UI is fragment-based: `IterableInboxFragment` requires a +> `FragmentManager`, so its host must be a `FragmentActivity` (or its descendant, +> `AppCompatActivity`). Compose-first apps often use a plain `ComponentActivity` as +> their host, which has no `FragmentManager`—hosting the inbox fragment there +> crashes when the fragment is attached. If your app is Compose-first, change the +> host activity's base class to `FragmentActivity` / `AppCompatActivity` before +> adding the inbox. (Iterable's Android SDK doesn't currently provide a +> Compose-native inbox.) +> +> Note that this requirement applies to the inbox UI only. Starting with SDK +> version 3.9.0, in-app messages themselves render correctly in Compose-first +> apps. For more information, see [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231#displaying-in-app-messages-in-jetpack-compose-apps-sdk-v3-9-0-and-above). + In your app, show the mobile inbox when the user selects a specific tab or taps a particular button. @@ -66,13 +80,13 @@ a particular button. Use the provided `InboxActivity` wrapper: - Kotlin: + _Kotlin_ ```kotlin startActivity(Intent(context, IterableInboxActivity::class.java)) ``` - Java: + _Java_ ```java startActivity(new Intent(getContext(), IterableInboxActivity.class)); @@ -98,4 +112,4 @@ If you're not using one of Iterable's mobile SDKs: ## Customizing the mobile inbox To learn how to customize the mobile inbox in an Android app, read -[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039091471). +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). diff --git a/iterable-android/snapshot/setting-up-unknown-user-activation.md b/iterable-android/snapshot/setting-up-unknown-user-activation.md index 45a9db6..3aa5d52 100644 --- a/iterable-android/snapshot/setting-up-unknown-user-activation.md +++ b/iterable-android/snapshot/setting-up-unknown-user-activation.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/unknown-user-activation-dev/setting-up- source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 45d0ae07bce89a4a4156c3b5e46f6bd4136d41b2 fetched_at: 2026-05-25T15:11:49.365Z -polished_at: 2026-06-05T13:59:18.320Z +polished_at: 2026-08-03T20:42:14.575Z layer: a snippets: [] summary: Unknown User Activation makes it possible to learn about, message, and diff --git a/iterable-android/snapshot/tracking-events-with-iterables-mobile-sdks.md b/iterable-android/snapshot/tracking-events-with-iterables-mobile-sdks.md index 028a389..b55e60c 100644 --- a/iterable-android/snapshot/tracking-events-with-iterables-mobile-sdks.md +++ b/iterable-android/snapshot/tracking-events-with-iterables-mobile-sdks.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/event-tracking/tracking-events-with-ite source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 0dbb170bfbd574bf405b33990d2c288ad8dcd153 fetched_at: 2026-05-25T15:11:48.241Z -polished_at: 2026-06-05T13:59:18.319Z +polished_at: 2026-08-03T20:42:14.574Z layer: a snippets: - index: 0 diff --git a/iterable-android/snapshot/updating-user-profiles.md b/iterable-android/snapshot/updating-user-profiles.md index 15c27b1..cfa4755 100644 --- a/iterable-android/snapshot/updating-user-profiles.md +++ b/iterable-android/snapshot/updating-user-profiles.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/managing-user-profiles/updating-user-pr source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3cca4ebdd1ee9da638428e3ded39de472202fd94 fetched_at: 2026-05-25T15:11:47.716Z -polished_at: 2026-06-05T13:59:18.318Z +polished_at: 2026-08-03T20:42:14.573Z layer: a snippets: - index: 0 diff --git a/pipeline/config/android.yml b/pipeline/config/android.yml index cb84f86..3d75c6f 100644 --- a/pipeline/config/android.yml +++ b/pipeline/config/android.yml @@ -16,8 +16,8 @@ platform: android source: repo: Iterable/iterable-docs - ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d - ref_label: "master @ 2026-05-25" + ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 + ref_label: "master @ 2026-08-03" sdk: repo: Iterable/iterable-android-sdk diff --git a/pipeline/src/fetch.ts b/pipeline/src/fetch.ts index c496b98..3192650 100644 --- a/pipeline/src/fetch.ts +++ b/pipeline/src/fetch.ts @@ -102,7 +102,9 @@ function resolveConfigPath(platformArg: string | undefined): string { } function main() { - const configPath = resolveConfigPath(process.argv[2]); + // pnpm may forward the `--` separator through to the script, so drop it. + const args = process.argv.slice(2).filter((a) => a !== "--"); + const configPath = resolveConfigPath(args[0]); if (!existsSync(configPath)) { console.error(`Config not found: ${configPath}`); process.exit(1); diff --git a/pipeline/src/polish-layer-a.ts b/pipeline/src/polish-layer-a.ts index 57a68e5..b3cebe0 100644 --- a/pipeline/src/polish-layer-a.ts +++ b/pipeline/src/polish-layer-a.ts @@ -2,10 +2,10 @@ * Stage 2 — Polish, Layer A CLI. * * Reads sources//.md, applies the Layer A deterministic - * transforms, and writes the intermediate result to - * /.layer-a.md. The `.layer-a.md` suffix marks - * the file as Layer A only — Layer B (LLM polish) overwrites without the - * suffix when it lands. + * transforms, and writes the polished corpus to + * /.polished.md — the file every downstream stage + * (snapshot, validate, manifest) consumes. v1 has no Layer B, so Layer A + * output IS the polished corpus; there is no separate promote step. * * Usage: * pnpm polish:a # all articles in the config @@ -102,7 +102,7 @@ function main() { sdkArtifact: SDK_ARTIFACT, }; const { output, snippets } = applyLayerA(raw, ctx); - const outPath = resolve(outDir, `${article.slug}.layer-a.md`); + const outPath = resolve(outDir, `${article.slug}.polished.md`); writeFileSync(outPath, output, "utf8"); console.log(` write ${article.slug} (${snippets.length} snippet${snippets.length === 1 ? "" : "s"})`); } diff --git a/pipeline/src/set-source-ref.ts b/pipeline/src/set-source-ref.ts index 8f8cda3..24d9420 100644 --- a/pipeline/src/set-source-ref.ts +++ b/pipeline/src/set-source-ref.ts @@ -27,7 +27,8 @@ const CONFIG_DIR = resolve(REPO_ROOT, "pipeline/config"); const SHA_RE = /^[0-9a-f]{40}$/; function main(): void { - const [platform, sha, label] = process.argv.slice(2); + // pnpm may forward the `--` separator through to the script, so drop it. + const [platform, sha, label] = process.argv.slice(2).filter((a) => a !== "--"); if (!platform || !sha) { console.error("Usage: set-source-ref.ts [label]"); process.exit(1); diff --git a/polished/android/android-app-links.polished.md b/polished/android/android-app-links.polished.md index 7a02923..0a9fac1 100644 --- a/polished/android/android-app-links.polished.md +++ b/polished/android/android-app-links.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/deep-links/android-app-links/index.md source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3c933bbc8661bddfeaa8914f3dbb8233d98469c6 fetched_at: 2026-05-25T15:11:45.366Z -polished_at: 2026-06-05T14:02:08.697Z +polished_at: 2026-08-03T20:42:14.571Z layer: a snippets: - index: 0 diff --git a/polished/android/android-sdk.polished.md b/polished/android/android-sdk.polished.md index 3fb48cb..72d889b 100644 --- a/polished/android/android-sdk.polished.md +++ b/polished/android/android-sdk.polished.md @@ -8,10 +8,10 @@ title: Iterable's Android SDK source_url: https://support.iterable.com/hc/articles/360035019712 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/iterables-ios-and-android-sdks/android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: c0ebe8b736d334190bc4fcae820fa386642aa40d -fetched_at: 2026-05-25T15:11:39.936Z -polished_at: 2026-06-05T13:59:18.308Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: de67a132360a33146dae801ab62c3de1d6846ba9 +fetched_at: 2026-08-03T20:41:28.018Z +polished_at: 2026-08-03T20:42:14.561Z layer: a snippets: - index: 0 @@ -91,14 +91,46 @@ snippets: hash: 0450a5b49d68 line_count: 12 - index: 19 + lang: java + hash: e79d0e004450 + line_count: 4 + - index: 20 + lang: java + hash: acfa8af81cce + line_count: 5 + - index: 21 + lang: kotlin + hash: 18b42ba68e56 + line_count: 5 + - index: 22 + lang: java + hash: a16ba32ab078 + line_count: 3 + - index: 23 + lang: java + hash: 6f27d743e5d6 + line_count: 1 + - index: 24 + lang: java + hash: eaacdddcb017 + line_count: 1 + - index: 25 + lang: java + hash: 6b21444c769f + line_count: 5 + - index: 26 + lang: java + hash: 2f3eb2cbfb4e + line_count: 5 + - index: 27 lang: java hash: 1ee664bef567 line_count: 5 - - index: 20 + - index: 28 lang: java hash: 9edce984f563 line_count: 4 - - index: 21 + - index: 29 lang: groovy hash: 744057b87f30 line_count: 8 @@ -117,7 +149,7 @@ higher. ## Encrypted data Depending on your `minSdkVersion`, Iterable's Android SDK can encrypt some -data at rest. For more information, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +data at rest. For more information, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). ## Installing the SDK @@ -125,7 +157,7 @@ Follow these steps to install Iterable's Android SDK. If you're upgrading from a previous version, see [Upgrading the SDK](#upgrading-the-sdk). > [!WARNING] -> If your app targets API level 22 or lower, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10) +> If your app targets API level 22 or lower, read [Upgrading to 3.4.10](#upgrading-to-3-4-10) > to learn about some adjustments you'll need to make to your Android project. ### Step 1: Define a mobile app and push integration in Iterable @@ -309,7 +341,7 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` -For more information about this option, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +For more information about this option, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). #### Step 5.5: Specify a push integration name, if necessary @@ -504,10 +536,13 @@ as configured by your `RetryPolicy`, it stops attempting to refresh the JWT toke > > In addition to the `RetryPolicy` above (which controls JWT refresh scheduling), > the SDK supports automatic retry for offline-queued tasks that fail due to JWT -> expiration. When enabled via remote configuration, the offline task runner -> pauses authenticated tasks on a 401 error, refreshes the JWT, and retries +> expiration. When this feature is enabled, the offline task runner pauses +> authenticated tasks on a 401 error, refreshes the JWT, and retries > automatically. Unauthenticated API calls continue processing while > authentication is paused. This feature requires no code changes. +> +> This feature is not enabled by default. To turn it on for your project, ask +> your Iterable customer success manager to enable it for your account. It's also possible to _manually_ pause JWT token refresh attempts. To do this, call: @@ -657,7 +692,354 @@ To learn how to use Iterable's Android SDK with Embedded Messaging, read This section describes how to upgrade from earlier versions of Iterable's Android SDK. -### Upgrading to 3.5.12+ +### Upgrading to 3.10.0 + +[Version 3.10.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.10.0) +of Iterable's Android SDK makes manager getters fail gracefully before +initialization, and adds a `DEFER` response for in-app handlers, a +`resumeInAppDisplay()` method, and unknown user criteria fetch callbacks. +**No action is required to upgrade**—all of these changes are backward +compatible. + +#### Manager getters no longer crash before initialization + +In earlier versions, calling `getInAppManager()` or `getEmbeddedManager()` +before `IterableApi.initialize()` threw a `RuntimeException`, which could crash +the host app. Starting with version 3.10.0, these methods log an error and +return a no-op manager instead—it returns empty results and ignores commands, so +a call-ordering mistake no longer crashes your app. + +If you need to detect whether the SDK is initialized before using a manager, use +the new `getInAppManagerOrNull()` and `getEmbeddedManagerOrNull()` methods, which +return `null` (rather than a no-op manager) when the SDK isn't initialized yet. + +```java +IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManagerOrNull(); +if (inAppManager != null) { + // Safe to use; the SDK is initialized. +} +``` + +As always, initialize the SDK in the `onCreate` method of your `Application` +class before calling other SDK methods. + +#### New: `DEFER` response and `resumeInAppDisplay()` for in-app messages + +`IterableInAppHandler.InAppResponse` now includes a `DEFER` value. Unlike `SKIP` +(which permanently drops a message), `DEFER` keeps the message pending so the +SDK reconsiders it later—useful for temporary suppression, such as while a +splash screen is showing. To re-check pending messages on demand once your app +is ready, call the new `IterableInAppManager.resumeInAppDisplay()` method. For +more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231). + +#### New: unknown user criteria fetch callbacks + +`IterableUnknownUserHandler` now reports the results of unknown user criteria +fetches through two optional methods: `onCriteriaReceived(JSONObject criteria)` +on success and `onCriteriaFetchFailed(String reason)` on failure. Both have +default, no-op implementations, so existing handlers are unaffected. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.9.0 + +[Version 3.9.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.9.0) +of Iterable's Android SDK adds in-app message support for Jetpack Compose apps, +a new opt-in toolbar for the mobile inbox, and additional context for push-open +tracking. **No action is required to upgrade**—all of these changes are +backward compatible. + +#### In-app messages in Jetpack Compose apps + +The SDK can now render in-app messages using a new `Dialog`-based renderer +(`IterableInAppDialogNotification`) that doesn't require a `FragmentActivity`. +Apps that host in-app messages in a `FragmentActivity` continue to use the +existing `Fragment`-based rendering; apps that don't (such as those built fully +with Jetpack Compose, using a `ComponentActivity`) automatically fall back to +the `Dialog`-based renderer. As a result, in-app messages now display correctly +in apps built fully with Jetpack Compose, with no additional setup. + +#### New: `IterableInboxToolbarView` for the mobile inbox + +If you use Iterable's [Mobile Inbox](https://support.iterable.com/hc/articles/360038744152), +you can now add an optional toolbar above the inbox list using the new +`IterableInboxToolbarView`. Configure it with the `InboxToolbarOption` sealed +interface: + +- `None` (default) — No toolbar. The inbox behaves exactly as it did in + previous SDK versions. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To override it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. Views tagged with the + reserved IDs `@id/iterable_reserved_inbox_toolbar_action` and + `@id/iterable_reserved_inbox_toolbar_title` are automatically wired to the + SDK's back handler and title binding, respectively (both are optional). + +Configure the toolbar programmatically with `IterableInboxFragment.newInstance(...)` +(using the new two- or six-argument overloads), or with `IterableInboxActivity` +intent extras (`TOOLBAR_OPTION` and `TOOLBAR_TITLE`). + +> [!WARNING] +> When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +> descendant. + +For more information about customizing the inbox, see +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +#### New: `appAlreadyRunning` field on `trackPushOpen` + +`trackPushOpen` now includes an `appAlreadyRunning` field that indicates whether +the app was already running when the push notification was received. A new +`trackPushOpen(int, int, String, boolean, JSONObject)` overload lets you pass +this value; existing overloads default it to `false`, so no changes are required +for existing code. + +#### Fix: `TransactionTooLargeException` crash for large in-app messages + +This release also fixes a `TransactionTooLargeException` crash that could occur +when displaying in-app messages with oversized HTML payloads. The HTML is no +longer serialized into the fragment's saved instance state—it's reloaded from +storage when the fragment is recreated. In-app messages with missing HTML now +dismiss gracefully without registering tracking events, and a warning is logged +for HTML payloads that exceed the recommended size. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +### Upgrading to 3.8.0 + +[Version 3.8.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.8.0) +of Iterable's Android SDK introduces a new configuration option for controlling +how in-app messages interact with system bars, plus refinements to embedded +message views and a security cleanup. **No action is required for most apps**—upgrading +preserves the existing in-app message behavior introduced in 3.6.1. + +#### New: `IterableInAppDisplayMode` for in-app messages + +Since 3.6.1, Iterable's Android SDK has always rendered in-app messages +edge-to-edge, behind the status bar and navigation bar. Starting with 3.8.0, +you can change that behavior globally by setting an `IterableInAppDisplayMode` +on `IterableConfig`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FORCE_RESPECT_BOUNDS) + .build(); + +IterableApi.initialize(context, apiKey, config); +``` + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Draws in-app content behind the system + bars, with transparent status and navigation bars. Preserves the behavior + introduced in SDK 3.6.1. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current system bar + configuration. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never overlaps system bars, + keeping UI elements like the close button always accessible. + +If the close button on your fullscreen in-app messages is being obscured by +the status bar on certain devices, switch to `FOLLOW_APP_LAYOUT` or +`FORCE_RESPECT_BOUNDS`. For more information, see [Configuring how in-app messages interact with system bars](https://support.iterable.com/hc/articles/360035537231#configuring-how-in-app-messages-interact-with-system-bars-sdk-v3-8-0-and-above) +in the In-App Messages on Android documentation. + +#### Other changes in 3.8.0 + +- **`imageScaleType` option for embedded message views**: `IterableEmbeddedViewConfig` + exposes a new `imageScaleType` property that controls how the image is + scaled within the 16:9 container of an out-of-the-box embedded message view. + +- **Default values for `IterableEmbeddedViewConfig` parameters**: All + `IterableEmbeddedViewConfig` constructor parameters now have default values, + so you only need to specify the styling options you want to customize. + Existing calls that pass every parameter continue to work unchanged. + +- **Embedded message card layout fixes**: Out-of-the-box embedded message + views render correctly again on cards. The image now displays at a 16:9 + aspect ratio instead of collapsing to zero height, the card container no + longer expands to fill its parent, the missing end margin on the card is + applied, bottom spacing on buttons is no longer cut off, and the image is + properly clipped to the card's rounded corners. + +- **Removed insecure `AES/CBC/PKCS5Padding` encryption**: `IterableDataEncryptor` + now exclusively uses `AES/GCM/NoPadding`. The legacy CBC algorithm was only + used on Android versions below KitKat (API 19), which have been unsupported + since `minSdkVersion` was raised to 21 in SDK 3.5.12. No data migration is + required. + +### Upgrading to 3.7.0 + +[Version 3.7.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.7.0) +introduces two opt-in improvements: an automatic JWT-refresh-and-retry flow for the +offline event queue, and new callbacks for tracking embedded message sync results. +No application code changes are required to upgrade—both improvements are opt-in. + +#### Opt-in: Auto-retry for JWT failures in offline event processing + +When offline event processing is enabled and a queued API call returns a 401 +JWT error, the SDK can now automatically: + +1. Pause processing of authenticated tasks in the offline queue. +2. Refresh the JWT via your registered `IterableAuthHandler`. +3. Retry the failed task with the new token. + +Unauthenticated endpoints (such as `disableDevice`, `mergeUser`, and +`trackConsent`) continue to be processed while authentication is paused, so +unrelated traffic isn't blocked behind a stale token. + +This behavior is disabled by default for existing customers. To enable it for your +project, talk to your Iterable customer success manager. No application code +changes are required once the flag is enabled—the SDK starts using the new behavior +automatically. + +#### Opt-in: Embedded messaging sync callbacks + +`IterableEmbeddedUpdateHandler` now exposes two optional callbacks— +`onEmbeddedMessagingSyncSucceeded()` and `onEmbeddedMessagingSyncFailed(reason)`— +that let your app react to embedded message syncs. Use them to stop a loading +spinner on success or to show fallback content on failure. Both methods have +default empty implementations, so existing code keeps working unchanged. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652#step-8-set-up-sdk-listeners). + +### Upgrading to 3.6.6 + +[Version 3.6.6](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.6) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.5 + +Starting with [version 3.6.5](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.5), +the `IterableEmbeddedView` constructor is **deprecated** because it violates +Android Fragment best practices: the system can't recreate the fragment after +configuration changes or process death, which can cause crashes. + +Use the `newInstance` factory method instead: + +```kotlin +// Deprecated: +val messageView = IterableEmbeddedView(ootbType, message, config) + +// Use this instead: +val messageView = IterableEmbeddedView.newInstance(ootbType, message, config) +``` + +The old constructor still works, but it's marked as deprecated and will be +removed in a future SDK release. Update your application code now to avoid a +breaking change later. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652). + +### Upgrading to 3.6.4 + +[Version 3.6.4](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.4) +makes the `isIterableDeeplink` method public so you can now check whether a URL is +an Iterable deep link before handling it. The method returns `true` when the URL +matches the Iterable deep link pattern (URLs containing `/a/` in the path). + +`isIterableDeeplink` is a **static** method on `IterableApi`: + +```java +if (IterableApi.isIterableDeeplink(urlString)) { + // URL is an Iterable deep link +} +``` + +For more information about deep links in Iterable, read [Android App Links](https://support.iterable.com/hc/articles/360035127392). + +### Upgrading to 3.6.3 + +[Version 3.6.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.3) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.2 + +[Version 3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) +adds three opt-in capabilities. No action is required to upgrade. + +- **Background initialization to prevent ANRs**: To run SDK initialization on + a background thread (with API calls automatically queued until ready), call + the new `IterableApi.initializeInBackground()` static method instead of + `IterableApi.initialize()`: + + ```java + IterableApi.initializeInBackground(context, apiKey, config, callback); + ``` + + Use this if running initialization on the main thread is contributing to + Application Not Responding (ANR) errors during app startup. The optional + `callback` (an `IterableInitializationCallback`) is invoked when + initialization completes. + +- **`onSDKInitialized()` callback**: A new static method on `IterableApi` lets + you subscribe a callback to be notified when initialization completes. Use + it when you need to defer SDK-dependent work from multiple call sites—for + example, posting the first event only after the SDK is fully ready. + + ```java + IterableApi.onSDKInitialized(callback); + ``` + +- **`setWebViewBaseUrl()` configuration option**: A new `IterableConfig.Builder` + method that sets the base URL used by WebView-based messages (in-app + messages, inbox, and embedded messages). Set it when you self-host custom + fonts or other external resources that require CORS to load successfully in + a WebView: + + ```java + IterableConfig config = new IterableConfig.Builder() + .setWebViewBaseUrl("https://your-cdn.example.com") + .build(); + + IterableApi.initialize(context, apiKey, config); + ``` + + If not set, the base URL defaults to an empty string (the original behavior). + +### Upgrading to 3.6.1 + +Starting with [version 3.6.1](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.1), +in-app messages render edge-to-edge so they display properly on devices with notches, +cutouts, and system bars. + +By default, the SDK applies white insets to fill the area behind the system +bars. In dark-themed apps, that white can contrast sharply with your in-app +message content. + +If your app uses a dark theme, consider updating the [background overlay](https://support.iterable.com/hc/articles/360044425951#background-overlay) +on your in-app templates to a color that complements your app, and test +existing templates before publishing. + +### Upgrading to 3.6.0 + +To enable Unknown User Activation, upgrade to [version 3.6.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.0) +of Iterable's Android SDK and call `setEnableUnknownUserActivation(true)` on +`IterableConfig.Builder` before initializing the SDK. These code changes are +only required if you want to use Unknown User Activation; otherwise, no +changes are required. + +```java +IterableConfig config = new IterableConfig.Builder() + .setEnableUnknownUserActivation(true) + .build(); + +IterableApi.initialize(context, "", config); +``` + +The SDK also captures user consent on your behalf when this feature is enabled. For full +setup instructions, read [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.5.12 - **Supported Android versions**: Beginning with [version 3.5.12](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.12), Iterable's Android SDK supports Android versions 5.0 (API level 21) and @@ -675,7 +1057,7 @@ Android SDK. IterableApi.initialize(context, apiKey, config); ``` -### Upgrading to 3.5.3+ +### Upgrading to 3.5.3 Starting with [version 3.5.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.3), Iterable's Android SDK provides more insight into JWT refresh failures, to help @@ -691,9 +1073,9 @@ If you've implemented that method, you'll need to update your application code. For more information, see [Step 5.6.1: Register an auth handler](#step-5-6-1-register-an-auth-handler). -### Upgrading to 3.5.2+ +### Upgrading to 3.5.2 -When upgrading to [version 3.5.2+](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) +When upgrading to [version 3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) of the SDK, you can make use of the `setAuthRetryPolicy` method on `IterableConfig` to specify: @@ -702,7 +1084,7 @@ to specify: - The interval between each retry attempt. Defaults to 6 seconds. - A backoff strategy: linear or exponential. Defaults to linear. -### Upgrading to 3.4.10+ +### Upgrading to 3.4.10 In Android apps with `minSdkVersion` 23 or higher ([Android 6.0](https://developer.android.com/studio/releases/platforms#6.0)) Iterable's Android SDK now encrypts the following fields when storing them at @@ -753,9 +1135,9 @@ encrypt data): - Add `multiDexEnabled true` to the `default` object, under `android`. - Add `implementation androidx.multidex:multidex:2.0.1` to the `dependencies`. -### Upgrading to 3.4.0+ +### Upgrading to 3.4.0 -- Starting with version [`3.4.0`](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) +- Starting with version [3.4.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) of Iterable's Android SDK, you'll need to declare the URL protocols that the SDK should expect to see on incoming links (and then handle as needed). For more information, read about [Step 5.3: Set allowed URL protocols](#step-5-3-set-allowed-url-protocols), @@ -766,7 +1148,7 @@ encrypt data): need to first grab an instance of the `IterableApi` class by calling `IterableApi.getInstance()`. For example, `IterableApi.getInstance().handleAppLink(...)`. -### Upgrading to 3.3.1+ +### Upgrading to 3.3.1 To resolve a breaking change introduced in Firebase Cloud Messaging [version 22.0.0](https://firebase.google.com/support/release-notes/android#messaging_v22-0-0), @@ -788,9 +1170,9 @@ android { } ``` -### Upgrading to 3.2.0+ +### Upgrading to 3.2.0 -[Versions 3.2.0+ of the SDK](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) +[Versions 3.2.0 and higher](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) depend on the [AndroidX](https://developer.android.com/jetpack/androidx) support libraries. To use these versions, you'll need to [migrate your app to use AndroidX](https://developer.android.com/jetpack/androidx/migrate). diff --git a/polished/android/configure-the-android-sdk.polished.md b/polished/android/configure-the-android-sdk.polished.md index d946199..2908576 100644 --- a/polished/android/configure-the-android-sdk.polished.md +++ b/polished/android/configure-the-android-sdk.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/unknown-user-activation-dev/configure-t source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: fa441fa69f35c816affd3df2d565dbfbd2727ca3 fetched_at: 2026-05-25T15:11:48.790Z -polished_at: 2026-06-05T13:59:18.319Z +polished_at: 2026-08-03T20:42:14.575Z layer: a snippets: - index: 0 diff --git a/polished/android/customizing-mobile-inbox-on-android.polished.md b/polished/android/customizing-mobile-inbox-on-android.polished.md index 3b08f11..62e83de 100644 --- a/polished/android/customizing-mobile-inbox-on-android.polished.md +++ b/polished/android/customizing-mobile-inbox-on-android.polished.md @@ -5,13 +5,13 @@ archetype: feature sdk_min_version: 3.7.0 sdk_artifact: iterableapi title: Customizing Mobile Inbox on Android -source_url: https://iterable.zendesk.com/hc/articles/360039189931 +source_url: https://support.iterable.com/hc/articles/360039189931 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/customizing-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 502be69e2212a0540bcaaad80299d4e3936bb656 -fetched_at: 2026-05-25T15:11:42.777Z -polished_at: 2026-06-05T13:59:18.313Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: e156e9f1bf13e4b41507a53dc10093422c8aefac +fetched_at: 2026-08-03T20:41:29.489Z +polished_at: 2026-08-03T20:42:14.567Z layer: a snippets: - index: 0 @@ -35,42 +35,58 @@ snippets: hash: 00a988c5c250 line_count: 3 - index: 5 + lang: kotlin + hash: d7b7d23c4c48 + line_count: 4 + - index: 6 + lang: java + hash: d46242e73662 + line_count: 4 + - index: 7 + lang: kotlin + hash: 689b12ae918a + line_count: 4 + - index: 8 + lang: java + hash: 584a986d1076 + line_count: 4 + - index: 9 lang: kotlin hash: edcae1e6e4de line_count: 13 - - index: 6 + - index: 10 lang: java hash: ef4f78408c7b line_count: 18 - - index: 7 + - index: 11 lang: kotlin hash: d121dfcd6053 line_count: 6 - - index: 8 + - index: 12 lang: kotlin hash: 6bb02c0de57c line_count: 10 - - index: 9 + - index: 13 lang: java hash: 80dd3262733b line_count: 13 - - index: 10 + - index: 14 lang: kotlin hash: 3cbc544d3efa line_count: 8 - - index: 11 + - index: 15 lang: kotlin hash: 8a0fe4b9b840 line_count: 10 - - index: 12 + - index: 16 lang: java hash: 8f7a8b76a82d line_count: 13 - - index: 13 + - index: 17 lang: kotlin hash: a28bb9940c6d line_count: 48 - - index: 14 + - index: 18 lang: java hash: 1ff85a5943c1 line_count: 53 @@ -157,7 +173,7 @@ default) or as standalone activities. To change this setting, either: - Set an extra for the activity's intent: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -165,7 +181,7 @@ default) or as standalone activities. To change this setting, either: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -175,13 +191,13 @@ default) or as standalone activities. To change this setting, either: - Pass constructor parameters to the fragment: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY, 0) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY) @@ -192,7 +208,7 @@ default) or as standalone activities. To change this setting, either: When launching the mobile inbox as an activity, change the title by passing an `activityTitle` argument in the intent: -Kotlin: +_Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -200,7 +216,7 @@ intent.putExtra("activityTitle", "My Inbox") startActivity(intent) ``` -Java: +_Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -208,7 +224,79 @@ intent.putExtra("activityTitle", "My Inbox"); startActivity(intent); ``` -### Cell layout, colors and font +### Inbox toolbar (SDK v3.9.0 and above) + +Starting with SDK version 3.9.0, you can display an optional toolbar above the +inbox list using `IterableInboxToolbarView`. The toolbar is off by default, +so the inbox behaves exactly as it did in previous SDK versions unless you opt +in. + +Configure the toolbar with the `InboxToolbarOption` sealed interface, which has +these options: + +- `None` (default) — No toolbar. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To customize it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. To wire your layout to + the SDK, tag views with these reserved IDs (both are optional): + - `@id/iterable_reserved_inbox_toolbar_action` — Automatically wired to the + SDK's back handler. + - `@id/iterable_reserved_inbox_toolbar_title` — Automatically bound to the + toolbar title. + +> [!WARNING] +> When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +> descendant. + +#### Configure the toolbar on the fragment + +Pass an `InboxToolbarOption` (and, optionally, a title) to +`IterableInboxFragment.newInstance(...)`: + +_Kotlin_ + +```kotlin +val inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton, + "My Inbox" +) +``` + +_Java_ + +```java +IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton.INSTANCE, + "My Inbox" +); +``` + +#### Configure the toolbar on the activity + +When launching the inbox as an activity, set the `TOOLBAR_OPTION` and +`TOOLBAR_TITLE` intent extras: + +_Kotlin_ + +```kotlin +val intent = Intent(context, IterableInboxActivity::class.java) +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton) +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox") +startActivity(intent) +``` + +_Java_ + +```java +Intent intent = new Intent(getContext(), IterableInboxActivity.class); +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton.INSTANCE); +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox"); +startActivity(intent); +``` + +### Cell layout, colors, and font > [!TIP] > In the [sample app](#sample-app), tap **Inbox with Custom Cell** to see an @@ -225,7 +313,7 @@ To modify the font, color or layout of inbox cells: 3. Specify this layout ID when launching the activity: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -233,7 +321,7 @@ To modify the font, color or layout of inbox cells: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -243,13 +331,13 @@ To modify the font, color or layout of inbox cells: 4. Alternatively, create the fragment with custom parameters: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item); @@ -266,7 +354,7 @@ subclass `IterableInboxFragment` and set a date mapper in `onCreate`. The date mapper takes an `IterableInAppMessage` and returns a string representing the creation date of the message. If the date field should be blank, return `null`. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxDateMapperFragment : IterableInboxFragment() { @@ -284,7 +372,7 @@ class CustomInboxDateMapperFragment : IterableInboxFragment() { } ``` -Java: +_Java_ ```java public class CustomInboxDateMapperJavaFragment extends IterableInboxFragment implements IterableInboxDateMapper { @@ -321,7 +409,7 @@ the message, `false` otherwise. `IterableInboxFilter` is an interface that declares a filter method. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFilterFragment : IterableInboxFragment() { @@ -347,7 +435,7 @@ class CustomInboxFilterFragment : IterableInboxFragment(), IterableInboxFilter { } ``` -Java: +_Java_ ```java public class CustomInboxFilterFragment extends IterableInboxFragment implements IterableInboxFilter { @@ -380,7 +468,7 @@ set a comparator in `onCreate`. `IterableInboxComparator` is a standard Java `Comparator` interface: return a negative integer, zero, or a positive integer when the first message is less than, equal to, or greater than the second. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxComparatorFragment : IterableInboxFragment() { @@ -480,7 +568,7 @@ To display different inbox items with different interfaces, follow these steps: 5. The same constants will then be passed to `getLayoutForViewType`. Use them to return different layouts based on the view type. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterExtension { @@ -533,7 +621,7 @@ class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterE } ``` -Java: +_Java_ ```java public class CustomInboxFieldsJavaFragment extends IterableInboxFragment implements IterableInboxAdapterExtension { diff --git a/polished/android/deep-linking-with-partners.polished.md b/polished/android/deep-linking-with-partners.polished.md index 5b3506d..6129dbe 100644 --- a/polished/android/deep-linking-with-partners.polished.md +++ b/polished/android/deep-linking-with-partners.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/deep-links/deep-linking-with-partners/i source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 7473a924f2b7eac5a08f7ec66c3fbf60d07089e4 fetched_at: 2026-05-25T15:11:46.035Z -polished_at: 2026-06-05T13:59:18.317Z +polished_at: 2026-08-03T20:42:14.572Z layer: a snippets: [] summary: "Iterable supports deep linking without any third-party diff --git a/polished/android/embedded-messages-with-iterables-android-sdk.polished.md b/polished/android/embedded-messages-with-iterables-android-sdk.polished.md index 0da103a..4c67603 100644 --- a/polished/android/embedded-messages-with-iterables-android-sdk.polished.md +++ b/polished/android/embedded-messages-with-iterables-android-sdk.polished.md @@ -8,10 +8,10 @@ title: Embedded Messages with Iterable's Android SDK source_url: https://support.iterable.com/hc/articles/23061877893652 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/embedded-messaging/embedded-messages-with-iterables-android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 6a6d6b5baf76973d611fb7f17785d3b04feeace0 -fetched_at: 2026-05-25T15:11:44.823Z -polished_at: 2026-06-05T13:59:18.315Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 576150056520b366d5190411e9f28198e70bbeaf +fetched_at: 2026-08-03T20:41:31.068Z +polished_at: 2026-08-03T20:42:14.570Z layer: a snippets: - index: 0 @@ -52,41 +52,45 @@ snippets: line_count: 23 - index: 9 lang: kotlin - hash: b21729c5cb5d - line_count: 13 + hash: b259e6a45e17 + line_count: 14 - index: 10 + lang: kotlin + hash: a2932531b4f0 + line_count: 4 + - index: 11 lang: kotlin hash: 38e520b049c7 line_count: 1 - - index: 11 + - index: 12 lang: kotlin hash: 48c85c7cb874 line_count: 2 - - index: 12 + - index: 13 lang: xml hash: d9b27fbb15f5 line_count: 12 - - index: 13 + - index: 14 lang: kotlin hash: 3887b4569179 line_count: 3 - - index: 14 + - index: 15 lang: kotlin hash: b64c0dd160ee line_count: 7 - - index: 15 + - index: 16 lang: kotlin hash: a8b0449c17f6 line_count: 13 - - index: 16 + - index: 17 lang: kotlin hash: 97d5fb776fc9 line_count: 7 - - index: 17 + - index: 18 lang: kotlin hash: 1048049cb459 line_count: 8 - - index: 18 + - index: 19 lang: kotlin hash: 820e05f6b034 line_count: 7 @@ -529,19 +533,45 @@ to declare the styles you'd like the view to use: ```kotlin // Grab your app's colors from wherever it makes sense. val config = IterableEmbeddedViewConfig( - backgroundColor: Color.parseColor("#FFFFFF"), - borderColor: Color.parseColor("#000000"), - borderWidth: 1, - borderCornerRadius: 8f, - primaryBtnBackgroundColor: Color.parseColor("#0000FF"), - primaryBtnTextColor: Color.parseColor("#FFFFFF"), - secondaryBtnBackgroundColor: Color.parseColor("#FFFFFF"), - secondaryBtnTextColor: Color.parseColor("#000000"), - titleTextColor: Color.parseColor("#000000"), - bodyTextColor: Color.parseColor("#000000") + backgroundColor = Color.parseColor("#FFFFFF"), + borderColor = Color.parseColor("#000000"), + borderWidth = 1, + borderCornerRadius = 8f, + primaryBtnBackgroundColor = Color.parseColor("#0000FF"), + primaryBtnTextColor = Color.parseColor("#FFFFFF"), + secondaryBtnBackgroundColor = Color.parseColor("#FFFFFF"), + secondaryBtnTextColor = Color.parseColor("#000000"), + titleTextColor = Color.parseColor("#000000"), + bodyTextColor = Color.parseColor("#000000"), + imageScaleType = ImageView.ScaleType.CENTER_CROP +) +``` + +**💡 TIP — Default values (SDK v3.8.0 and above)** + +Starting with SDK version 3.8.0, all `IterableEmbeddedViewConfig` parameters +have default values, so you only need to specify the styling options you want +to customize. The example above shows every option for reference, but you can pass +just the ones you need. For example: + +```kotlin +val config = IterableEmbeddedViewConfig( + backgroundColor = Color.parseColor("#FFFFFF"), + borderCornerRadius = 8f ) ``` +All color, border, and text-color parameters default to `null` (which falls +back to the view's built-in styling). The `imageScaleType` parameter defaults +to `ImageView.ScaleType.CENTER_CROP`. + +The `imageScaleType` parameter (added in SDK v3.8.0) controls how the image is +scaled within the 16:9 image container of `CARD` and `BANNER` views. It accepts +any standard Android [`ImageView.ScaleType`](https://developer.android.com/reference/android/widget/ImageView.ScaleType) +value (for example, `CENTER_CROP`, `FIT_CENTER`, or `FIT_XY`). The +`NOTIFICATION` view type does not display an image, so this parameter has no +effect on that view type. + Then, when it's time to display a message, create the `IterableEmbeddedView` using the `newInstance` factory method: diff --git a/polished/android/identifying-the-user.polished.md b/polished/android/identifying-the-user.polished.md index 22cbf0b..429f386 100644 --- a/polished/android/identifying-the-user.polished.md +++ b/polished/android/identifying-the-user.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/managing-user-profiles/identifying-the- source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: ced31ca29ce63d634a0c4691277a114ed3f0ceb9 fetched_at: 2026-05-25T15:11:46.888Z -polished_at: 2026-06-05T13:59:18.317Z +polished_at: 2026-08-03T20:42:14.572Z layer: a snippets: - index: 0 diff --git a/polished/android/in-app-messages-on-android.polished.md b/polished/android/in-app-messages-on-android.polished.md index e75489a..1e6edba 100644 --- a/polished/android/in-app-messages-on-android.polished.md +++ b/polished/android/in-app-messages-on-android.polished.md @@ -8,10 +8,10 @@ title: In-App Messages on Android source_url: https://support.iterable.com/hc/articles/360035537231 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/in-app-messages-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 67ade49d8716b4d65cdb7779eb210afa0b5054da -fetched_at: 2026-05-27T12:44:52.898Z -polished_at: 2026-06-05T13:59:18.311Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 65412ae773eaca59531243ff4775fd4457b5b608 +fetched_at: 2026-08-03T20:41:28.539Z +polished_at: 2026-08-03T20:42:14.565Z layer: a snippets: - index: 0 @@ -19,17 +19,33 @@ snippets: hash: 8db83c22f9a3 line_count: 18 - index: 1 + lang: java + hash: 41e8696aa8cc + line_count: 9 + - index: 2 lang: java hash: bcfe488ba3d1 line_count: 10 - - index: 2 + - index: 3 + lang: java + hash: a903f600bb27 + line_count: 4 + - index: 4 lang: kotlin hash: 28725ee7538d line_count: 1 - - index: 3 + - index: 5 lang: java hash: 265c585feb80 line_count: 1 + - index: 6 + lang: kotlin + hash: 34caae496955 + line_count: 1 + - index: 7 + lang: java + hash: e4f4f768e9bb + line_count: 1 summary: By default, when an in-app message arrives from the server, the SDK automatically shows it if the app is in the foreground. If an in-app message is already showing when the new message arrives, the new message will be shown @@ -83,6 +99,36 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` +### Deferring an in-app message (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, `onNewInApp` can also return +`InAppResponse.DEFER`. Unlike `SKIP`, which permanently drops the message, +`DEFER` keeps the message pending so the SDK reconsiders it on a later display +pass (for example, on the next foreground, sync, or newly arrived message). This +is useful for temporary, per-message suppression—for example, while a splash +screen is showing. + +```java +class MyInAppHandler implements IterableInAppHandler { + @Override + public InAppResponse onNewInApp(IterableInAppMessage message) { + if (appIsShowingSplashScreen()) { + return InAppResponse.DEFER; + } + return InAppResponse.SHOW; + } +} +``` + +Once your app is ready to display in-app messages, call +`resumeInAppDisplay()` (see [Pausing the display of in-app messages](#pausing-the-display-of-in-app-messages-sdk-v3-2-6-and-above)) +to re-check pending messages immediately, instead of waiting for the next +foreground or sync trigger. + +> [!NOTE] +> In Kotlin, add a `DEFER` branch to any exhaustive `when` expression over +> `InAppResponse`. + ## Getting the local queue of in-app messages The SDK keeps the local in-app message queue in sync by checking the server queue @@ -131,6 +177,69 @@ The SDK handles in-app message buttons and links as follows: returns `false` for the provided URL, the URL will be opened by the system (using a web browser or other application, as applicable). +## Configuring how in-app messages interact with system bars (SDK v3.8.0 and above) + +By default, Iterable's Android SDK draws in-app messages edge-to-edge, with +content extending behind the status bar and navigation bar. This was the only +behavior in SDK versions 3.6.1 through 3.7.0. + +Starting with SDK version 3.8.0, you can configure how in-app messages interact +with system bars by setting `IterableInAppDisplayMode` on `IterableConfig`. +This setting applies globally to all in-app messages displayed by the SDK. + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Forces in-app messages to display + edge-to-edge, drawing content behind the status bar and navigation bar. + This preserves the behavior of previous SDK versions. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current layout configuration. + If your app is edge-to-edge, in-app messages display edge-to-edge; if your + app respects system bar bounds, so do in-app messages. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. Uses the legacy `FLAG_FULLSCREEN` on API levels below 30 and + `WindowInsetsController` on API 30 and above. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never draws behind the + status bar or navigation bar, keeping UI elements like the close button + always accessible. + +To configure the display mode, call `setInAppDisplayMode()` on +`IterableConfig.Builder`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FOLLOW_APP_LAYOUT) + .build(); +IterableApi.initialize(context, "", config); +``` + +> [!TIP] +> If the close button (or other interactive elements) in your fullscreen in-app +> messages is being obscured by the status bar, switch to `FOLLOW_APP_LAYOUT` +> or `FORCE_RESPECT_BOUNDS`. + +## Displaying in-app messages in Jetpack Compose apps (SDK v3.9.0 and above) + +In SDK versions before 3.9.0, displaying an in-app message required a +`FragmentActivity`, because the SDK rendered in-app messages using a `Fragment`. +This meant that apps built fully with [Jetpack Compose](https://developer.android.com/compose) +(and without the Android fragment framework) couldn't display in-app messages. + +Starting with SDK version 3.9.0, the SDK can also render in-app messages using a +`Dialog`-based renderer (`IterableInAppDialogNotification`) that doesn't require +a `FragmentActivity`. When the current activity is a `FragmentActivity`, the SDK +continues to use the existing `Fragment`-based rendering; when it isn't (for +example, a Compose-based `ComponentActivity`), the SDK falls back to the +`Dialog`-based renderer. As a result, in-app messages now display correctly in +apps built fully with Jetpack Compose. + +No code changes are required to take advantage of this—just upgrade to SDK +version 3.9.0 or later. (The host must still be an `Activity`.) + +> [!WARNING] +> This Compose compatibility applies to **in-app message rendering** only. Iterable's +> mobile inbox UI is still fragment-based and requires a `FragmentActivity` host. For +> more information, see [Setting up Mobile Inbox on Android](https://support.iterable.com/hc/articles/360038744152#displaying-the-mobile-inbox). + ## Changing the display interval between in-app messages To customize the time delay between successive in-app messages, set @@ -162,4 +271,30 @@ However, it will keep the local queue of in-app messages in sync. > method on `IterableInAppManager` to manually display messages. To resume the display of in-app messages from your app's queue, call -`setAutoDisplayPaused(false)`. +`setAutoDisplayPaused(false)`. + +### Re-evaluating pending in-app messages on demand (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, you can call `resumeInAppDisplay()` to prompt +the SDK to re-evaluate pending in-app messages once your app is ready to display +them—for example, after a splash screen is dismissed, or after you deferred a +message by returning `InAppResponse.DEFER` from `onNewInApp` (see +[Deferring an in-app message](#deferring-an-in-app-message-sdk-v3-10-0-and-above)). +Without this call, the SDK re-checks pending messages only on its own triggers +(foreground, sync, or a newly arrived message). + +_Kotlin_ + +```kotlin +IterableApi.getInstance().inAppManager.resumeInAppDisplay() +``` + +_Java_ + +```java +IterableApi.getInstance().getInAppManager().resumeInAppDisplay(); +``` + +`resumeInAppDisplay()` is independent of `setAutoDisplayPaused(boolean)`: if +automatic display is paused, this call won't show anything (and logs a warning) +until you also call `setAutoDisplayPaused(false)`. diff --git a/polished/android/push-notification-overview.polished.md b/polished/android/push-notification-overview.polished.md index 5354a2d..6397f72 100644 --- a/polished/android/push-notification-overview.polished.md +++ b/polished/android/push-notification-overview.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/push-notifications/push-notification-ov source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3306f88835e0c1b30e4b4020287d772cfd93ba1e fetched_at: 2026-05-25T15:11:44.170Z -polished_at: 2026-06-05T13:59:18.315Z +polished_at: 2026-08-03T20:42:14.569Z layer: a snippets: [] summary: To alert users about updates, offers, content, and other information diff --git a/polished/android/setting-up-android-push-notifications.polished.md b/polished/android/setting-up-android-push-notifications.polished.md index 91da677..41f48f6 100644 --- a/polished/android/setting-up-android-push-notifications.polished.md +++ b/polished/android/setting-up-android-push-notifications.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/push-notifications/setting-up-android-p source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 45fa32087746810e08046766184fd4c2eb1acc94 fetched_at: 2026-05-25T15:11:43.429Z -polished_at: 2026-06-05T13:59:18.314Z +polished_at: 2026-08-03T20:42:14.568Z layer: a snippets: - index: 0 diff --git a/polished/android/setting-up-mobile-inbox-on-android.polished.md b/polished/android/setting-up-mobile-inbox-on-android.polished.md index cf38abd..36a6e21 100644 --- a/polished/android/setting-up-mobile-inbox-on-android.polished.md +++ b/polished/android/setting-up-mobile-inbox-on-android.polished.md @@ -8,10 +8,10 @@ title: Setting up Mobile Inbox on Android source_url: https://support.iterable.com/hc/articles/360038744152 source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/setting-up-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 968c6cc97c2aec2e8e4124b5ef18aa9a000a139d -fetched_at: 2026-05-25T15:11:41.984Z -polished_at: 2026-06-05T13:59:18.312Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 9b54bece973b76efd0e1eaec0494b0e9d2c2af7c +fetched_at: 2026-08-03T20:41:29.000Z +polished_at: 2026-08-03T20:42:14.566Z layer: a snippets: [] summary: Apps using version 3.2.0 and later of Iterable's [Android @@ -46,6 +46,20 @@ To add a mobile inbox to your Android app, first install Iterable's ## Displaying the mobile inbox +> [!WARNING] +> Iterable's mobile inbox UI is fragment-based: `IterableInboxFragment` requires a +> `FragmentManager`, so its host must be a `FragmentActivity` (or its descendant, +> `AppCompatActivity`). Compose-first apps often use a plain `ComponentActivity` as +> their host, which has no `FragmentManager`—hosting the inbox fragment there +> crashes when the fragment is attached. If your app is Compose-first, change the +> host activity's base class to `FragmentActivity` / `AppCompatActivity` before +> adding the inbox. (Iterable's Android SDK doesn't currently provide a +> Compose-native inbox.) +> +> Note that this requirement applies to the inbox UI only. Starting with SDK +> version 3.9.0, in-app messages themselves render correctly in Compose-first +> apps. For more information, see [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231#displaying-in-app-messages-in-jetpack-compose-apps-sdk-v3-9-0-and-above). + In your app, show the mobile inbox when the user selects a specific tab or taps a particular button. @@ -66,13 +80,13 @@ a particular button. Use the provided `InboxActivity` wrapper: - Kotlin: + _Kotlin_ ```kotlin startActivity(Intent(context, IterableInboxActivity::class.java)) ``` - Java: + _Java_ ```java startActivity(new Intent(getContext(), IterableInboxActivity.class)); @@ -98,4 +112,4 @@ If you're not using one of Iterable's mobile SDKs: ## Customizing the mobile inbox To learn how to customize the mobile inbox in an Android app, read -[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039091471). +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). diff --git a/polished/android/setting-up-unknown-user-activation.polished.md b/polished/android/setting-up-unknown-user-activation.polished.md index 45a9db6..3aa5d52 100644 --- a/polished/android/setting-up-unknown-user-activation.polished.md +++ b/polished/android/setting-up-unknown-user-activation.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/unknown-user-activation-dev/setting-up- source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 45d0ae07bce89a4a4156c3b5e46f6bd4136d41b2 fetched_at: 2026-05-25T15:11:49.365Z -polished_at: 2026-06-05T13:59:18.320Z +polished_at: 2026-08-03T20:42:14.575Z layer: a snippets: [] summary: Unknown User Activation makes it possible to learn about, message, and diff --git a/polished/android/tracking-events-with-iterables-mobile-sdks.polished.md b/polished/android/tracking-events-with-iterables-mobile-sdks.polished.md index 028a389..b55e60c 100644 --- a/polished/android/tracking-events-with-iterables-mobile-sdks.polished.md +++ b/polished/android/tracking-events-with-iterables-mobile-sdks.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/event-tracking/tracking-events-with-ite source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 0dbb170bfbd574bf405b33990d2c288ad8dcd153 fetched_at: 2026-05-25T15:11:48.241Z -polished_at: 2026-06-05T13:59:18.319Z +polished_at: 2026-08-03T20:42:14.574Z layer: a snippets: - index: 0 diff --git a/polished/android/updating-user-profiles.polished.md b/polished/android/updating-user-profiles.polished.md index 15c27b1..cfa4755 100644 --- a/polished/android/updating-user-profiles.polished.md +++ b/polished/android/updating-user-profiles.polished.md @@ -11,7 +11,7 @@ source_path: docs/developer-and-api-docs/managing-user-profiles/updating-user-pr source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d source_sha: 3cca4ebdd1ee9da638428e3ded39de472202fd94 fetched_at: 2026-05-25T15:11:47.716Z -polished_at: 2026-06-05T13:59:18.318Z +polished_at: 2026-08-03T20:42:14.573Z layer: a snippets: - index: 0 diff --git a/sources/android/android-sdk.md b/sources/android/android-sdk.md index 8c47031..73e2708 100644 --- a/sources/android/android-sdk.md +++ b/sources/android/android-sdk.md @@ -4,9 +4,9 @@ title: Iterable's Android SDK useInNovaDocs: true source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/iterables-ios-and-android-sdks/android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: c0ebe8b736d334190bc4fcae820fa386642aa40d -fetched_at: 2026-05-25T15:11:39.936Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: de67a132360a33146dae801ab62c3de1d6846ba9 +fetched_at: 2026-08-03T20:41:28.018Z --- ​ # Iterable's Android SDK @@ -25,7 +25,7 @@ higher. ## Encrypted data Depending on your `minSdkVersion`, Iterable's Android SDK can encrypt some -data at rest. For more information, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +data at rest. For more information, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). ## Installing the SDK @@ -33,7 +33,7 @@ Follow these steps to install Iterable's Android SDK. If you're upgrading from a previous version, see [Upgrading the SDK](#upgrading-the-sdk). :::warning IMPORTANT -If your app targets API level 22 or lower, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10) +If your app targets API level 22 or lower, read [Upgrading to 3.4.10](#upgrading-to-3-4-10) to learn about some adjustments you'll need to make to your Android project. ::: @@ -224,7 +224,7 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` -For more information about this option, read [Upgrading to 3.4.10+](#upgrading-to-3-4-10). +For more information about this option, read [Upgrading to 3.4.10](#upgrading-to-3-4-10). #### Step 5.5: Specify a push integration name, if necessary @@ -419,10 +419,13 @@ as configured by your `RetryPolicy`, it stops attempting to refresh the JWT toke :::tip Auto-retry for offline processing (3.7.0+) In addition to the `RetryPolicy` above (which controls JWT refresh scheduling), the SDK supports automatic retry for offline-queued tasks that fail due to JWT -expiration. When enabled via remote configuration, the offline task runner -pauses authenticated tasks on a 401 error, refreshes the JWT, and retries +expiration. When this feature is enabled, the offline task runner pauses +authenticated tasks on a 401 error, refreshes the JWT, and retries automatically. Unauthenticated API calls continue processing while authentication is paused. This feature requires no code changes. + +This feature is not enabled by default. To turn it on for your project, ask +your Iterable customer success manager to enable it for your account. ::: It's also possible to _manually_ pause JWT token refresh attempts. To do this, @@ -577,7 +580,355 @@ To learn how to use Iterable's Android SDK with Embedded Messaging, read This section describes how to upgrade from earlier versions of Iterable's Android SDK. -### Upgrading to 3.5.12+ +### Upgrading to 3.10.0 + +[Version 3.10.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.10.0) +of Iterable's Android SDK makes manager getters fail gracefully before +initialization, and adds a `DEFER` response for in-app handlers, a +`resumeInAppDisplay()` method, and unknown user criteria fetch callbacks. +**No action is required to upgrade**—all of these changes are backward +compatible. + +#### Manager getters no longer crash before initialization + +In earlier versions, calling `getInAppManager()` or `getEmbeddedManager()` +before `IterableApi.initialize()` threw a `RuntimeException`, which could crash +the host app. Starting with version 3.10.0, these methods log an error and +return a no-op manager instead—it returns empty results and ignores commands, so +a call-ordering mistake no longer crashes your app. + +If you need to detect whether the SDK is initialized before using a manager, use +the new `getInAppManagerOrNull()` and `getEmbeddedManagerOrNull()` methods, which +return `null` (rather than a no-op manager) when the SDK isn't initialized yet. + +```java +IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManagerOrNull(); +if (inAppManager != null) { + // Safe to use; the SDK is initialized. +} +``` + +As always, initialize the SDK in the `onCreate` method of your `Application` +class before calling other SDK methods. + +#### New: `DEFER` response and `resumeInAppDisplay()` for in-app messages + +`IterableInAppHandler.InAppResponse` now includes a `DEFER` value. Unlike `SKIP` +(which permanently drops a message), `DEFER` keeps the message pending so the +SDK reconsiders it later—useful for temporary suppression, such as while a +splash screen is showing. To re-check pending messages on demand once your app +is ready, call the new `IterableInAppManager.resumeInAppDisplay()` method. For +more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231). + +#### New: unknown user criteria fetch callbacks + +`IterableUnknownUserHandler` now reports the results of unknown user criteria +fetches through two optional methods: `onCriteriaReceived(JSONObject criteria)` +on success and `onCriteriaFetchFailed(String reason)` on failure. Both have +default, no-op implementations, so existing handlers are unaffected. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.9.0 + +[Version 3.9.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.9.0) +of Iterable's Android SDK adds in-app message support for Jetpack Compose apps, +a new opt-in toolbar for the mobile inbox, and additional context for push-open +tracking. **No action is required to upgrade**—all of these changes are +backward compatible. + +#### In-app messages in Jetpack Compose apps + +The SDK can now render in-app messages using a new `Dialog`-based renderer +(`IterableInAppDialogNotification`) that doesn't require a `FragmentActivity`. +Apps that host in-app messages in a `FragmentActivity` continue to use the +existing `Fragment`-based rendering; apps that don't (such as those built fully +with Jetpack Compose, using a `ComponentActivity`) automatically fall back to +the `Dialog`-based renderer. As a result, in-app messages now display correctly +in apps built fully with Jetpack Compose, with no additional setup. + +#### New: `IterableInboxToolbarView` for the mobile inbox + +If you use Iterable's [Mobile Inbox](https://support.iterable.com/hc/articles/360038744152), +you can now add an optional toolbar above the inbox list using the new +`IterableInboxToolbarView`. Configure it with the `InboxToolbarOption` sealed +interface: + +- `None` (default) — No toolbar. The inbox behaves exactly as it did in + previous SDK versions. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To override it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. Views tagged with the + reserved IDs `@id/iterable_reserved_inbox_toolbar_action` and + `@id/iterable_reserved_inbox_toolbar_title` are automatically wired to the + SDK's back handler and title binding, respectively (both are optional). + +Configure the toolbar programmatically with `IterableInboxFragment.newInstance(...)` +(using the new two- or six-argument overloads), or with `IterableInboxActivity` +intent extras (`TOOLBAR_OPTION` and `TOOLBAR_TITLE`). + +:::warning IMPORTANT +When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +descendant. +::: + +For more information about customizing the inbox, see +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +#### New: `appAlreadyRunning` field on `trackPushOpen` + +`trackPushOpen` now includes an `appAlreadyRunning` field that indicates whether +the app was already running when the push notification was received. A new +`trackPushOpen(int, int, String, boolean, JSONObject)` overload lets you pass +this value; existing overloads default it to `false`, so no changes are required +for existing code. + +#### Fix: `TransactionTooLargeException` crash for large in-app messages + +This release also fixes a `TransactionTooLargeException` crash that could occur +when displaying in-app messages with oversized HTML payloads. The HTML is no +longer serialized into the fragment's saved instance state—it's reloaded from +storage when the fragment is recreated. In-app messages with missing HTML now +dismiss gracefully without registering tracking events, and a warning is logged +for HTML payloads that exceed the recommended size. + +For more information, read [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231) +and [Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). + +### Upgrading to 3.8.0 + +[Version 3.8.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.8.0) +of Iterable's Android SDK introduces a new configuration option for controlling +how in-app messages interact with system bars, plus refinements to embedded +message views and a security cleanup. **No action is required for most apps**—upgrading +preserves the existing in-app message behavior introduced in 3.6.1. + +#### New: `IterableInAppDisplayMode` for in-app messages + +Since 3.6.1, Iterable's Android SDK has always rendered in-app messages +edge-to-edge, behind the status bar and navigation bar. Starting with 3.8.0, +you can change that behavior globally by setting an `IterableInAppDisplayMode` +on `IterableConfig`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FORCE_RESPECT_BOUNDS) + .build(); + +IterableApi.initialize(context, apiKey, config); +``` + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Draws in-app content behind the system + bars, with transparent status and navigation bars. Preserves the behavior + introduced in SDK 3.6.1. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current system bar + configuration. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never overlaps system bars, + keeping UI elements like the close button always accessible. + +If the close button on your fullscreen in-app messages is being obscured by +the status bar on certain devices, switch to `FOLLOW_APP_LAYOUT` or +`FORCE_RESPECT_BOUNDS`. For more information, see [Configuring how in-app messages interact with system bars](https://support.iterable.com/hc/articles/360035537231#configuring-how-in-app-messages-interact-with-system-bars-sdk-v3-8-0-and-above) +in the In-App Messages on Android documentation. + +#### Other changes in 3.8.0 + +- **`imageScaleType` option for embedded message views**: `IterableEmbeddedViewConfig` + exposes a new `imageScaleType` property that controls how the image is + scaled within the 16:9 container of an out-of-the-box embedded message view. + +- **Default values for `IterableEmbeddedViewConfig` parameters**: All + `IterableEmbeddedViewConfig` constructor parameters now have default values, + so you only need to specify the styling options you want to customize. + Existing calls that pass every parameter continue to work unchanged. + +- **Embedded message card layout fixes**: Out-of-the-box embedded message + views render correctly again on cards. The image now displays at a 16:9 + aspect ratio instead of collapsing to zero height, the card container no + longer expands to fill its parent, the missing end margin on the card is + applied, bottom spacing on buttons is no longer cut off, and the image is + properly clipped to the card's rounded corners. + +- **Removed insecure `AES/CBC/PKCS5Padding` encryption**: `IterableDataEncryptor` + now exclusively uses `AES/GCM/NoPadding`. The legacy CBC algorithm was only + used on Android versions below KitKat (API 19), which have been unsupported + since `minSdkVersion` was raised to 21 in SDK 3.5.12. No data migration is + required. + +### Upgrading to 3.7.0 + +[Version 3.7.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.7.0) +introduces two opt-in improvements: an automatic JWT-refresh-and-retry flow for the +offline event queue, and new callbacks for tracking embedded message sync results. +No application code changes are required to upgrade—both improvements are opt-in. + +#### Opt-in: Auto-retry for JWT failures in offline event processing + +When offline event processing is enabled and a queued API call returns a 401 +JWT error, the SDK can now automatically: + +1. Pause processing of authenticated tasks in the offline queue. +2. Refresh the JWT via your registered `IterableAuthHandler`. +3. Retry the failed task with the new token. + +Unauthenticated endpoints (such as `disableDevice`, `mergeUser`, and +`trackConsent`) continue to be processed while authentication is paused, so +unrelated traffic isn't blocked behind a stale token. + +This behavior is disabled by default for existing customers. To enable it for your +project, talk to your Iterable customer success manager. No application code +changes are required once the flag is enabled—the SDK starts using the new behavior +automatically. + +#### Opt-in: Embedded messaging sync callbacks + +`IterableEmbeddedUpdateHandler` now exposes two optional callbacks— +`onEmbeddedMessagingSyncSucceeded()` and `onEmbeddedMessagingSyncFailed(reason)`— +that let your app react to embedded message syncs. Use them to stop a loading +spinner on success or to show fallback content on failure. Both methods have +default empty implementations, so existing code keeps working unchanged. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652#step-8-set-up-sdk-listeners). + +### Upgrading to 3.6.6 + +[Version 3.6.6](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.6) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.5 + +Starting with [version 3.6.5](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.5), +the `IterableEmbeddedView` constructor is **deprecated** because it violates +Android Fragment best practices: the system can't recreate the fragment after +configuration changes or process death, which can cause crashes. + +Use the `newInstance` factory method instead: + +```kotlin +// Deprecated: +val messageView = IterableEmbeddedView(ootbType, message, config) + +// Use this instead: +val messageView = IterableEmbeddedView.newInstance(ootbType, message, config) +``` + +The old constructor still works, but it's marked as deprecated and will be +removed in a future SDK release. Update your application code now to avoid a +breaking change later. + +For more information, read [Embedded Messages with Iterable's Android SDK](https://support.iterable.com/hc/articles/23061877893652). + +### Upgrading to 3.6.4 + +[Version 3.6.4](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.4) +makes the `isIterableDeeplink` method public so you can now check whether a URL is +an Iterable deep link before handling it. The method returns `true` when the URL +matches the Iterable deep link pattern (URLs containing `/a/` in the path). + +`isIterableDeeplink` is a **static** method on `IterableApi`: + +```java +if (IterableApi.isIterableDeeplink(urlString)) { + // URL is an Iterable deep link +} +``` + +For more information about deep links in Iterable, read [Android App Links](https://support.iterable.com/hc/articles/360035127392). + +### Upgrading to 3.6.3 + +[Version 3.6.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.3) +of Iterable's Android SDK is a maintenance release. No action is required to +upgrade. + +### Upgrading to 3.6.2 + +[Version 3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) +adds three opt-in capabilities. No action is required to upgrade. + +- **Background initialization to prevent ANRs**: To run SDK initialization on + a background thread (with API calls automatically queued until ready), call + the new `IterableApi.initializeInBackground()` static method instead of + `IterableApi.initialize()`: + + ```java + IterableApi.initializeInBackground(context, apiKey, config, callback); + ``` + + Use this if running initialization on the main thread is contributing to + Application Not Responding (ANR) errors during app startup. The optional + `callback` (an `IterableInitializationCallback`) is invoked when + initialization completes. + +- **`onSDKInitialized()` callback**: A new static method on `IterableApi` lets + you subscribe a callback to be notified when initialization completes. Use + it when you need to defer SDK-dependent work from multiple call sites—for + example, posting the first event only after the SDK is fully ready. + + ```java + IterableApi.onSDKInitialized(callback); + ``` + +- **`setWebViewBaseUrl()` configuration option**: A new `IterableConfig.Builder` + method that sets the base URL used by WebView-based messages (in-app + messages, inbox, and embedded messages). Set it when you self-host custom + fonts or other external resources that require CORS to load successfully in + a WebView: + + ```java + IterableConfig config = new IterableConfig.Builder() + .setWebViewBaseUrl("https://your-cdn.example.com") + .build(); + + IterableApi.initialize(context, apiKey, config); + ``` + + If not set, the base URL defaults to an empty string (the original behavior). + +### Upgrading to 3.6.1 + +Starting with [version 3.6.1](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.1), +in-app messages render edge-to-edge so they display properly on devices with notches, +cutouts, and system bars. + +By default, the SDK applies white insets to fill the area behind the system +bars. In dark-themed apps, that white can contrast sharply with your in-app +message content. + +If your app uses a dark theme, consider updating the [background overlay](https://support.iterable.com/hc/articles/360044425951#background-overlay) +on your in-app templates to a color that complements your app, and test +existing templates before publishing. + +### Upgrading to 3.6.0 + +To enable Unknown User Activation, upgrade to [version 3.6.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.0) +of Iterable's Android SDK and call `setEnableUnknownUserActivation(true)` on +`IterableConfig.Builder` before initializing the SDK. These code changes are +only required if you want to use Unknown User Activation; otherwise, no +changes are required. + +```java +IterableConfig config = new IterableConfig.Builder() + .setEnableUnknownUserActivation(true) + .build(); + +IterableApi.initialize(context, "", config); +``` + +The SDK also captures user consent on your behalf when this feature is enabled. For full +setup instructions, read [Configure the Android SDK](https://support.iterable.com/hc/articles/40078934178836) +in the Unknown User Activation documentation. + +### Upgrading to 3.5.12 - **Supported Android versions**: Beginning with [version 3.5.12](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.12), Iterable's Android SDK supports Android versions 5.0 (API level 21) and @@ -595,7 +946,7 @@ Android SDK. IterableApi.initialize(context, apiKey, config); ``` -### Upgrading to 3.5.3+ +### Upgrading to 3.5.3 Starting with [version 3.5.3](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.3), Iterable's Android SDK provides more insight into JWT refresh failures, to help @@ -611,9 +962,9 @@ If you've implemented that method, you'll need to update your application code. For more information, see [Step 5.6.1: Register an auth handler](#step-5-6-1-register-an-auth-handler). -### Upgrading to 3.5.2+ +### Upgrading to 3.5.2 -When upgrading to [version 3.5.2+](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) +When upgrading to [version 3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) of the SDK, you can make use of the `setAuthRetryPolicy` method on `IterableConfig` to specify: @@ -622,7 +973,7 @@ to specify: - The interval between each retry attempt. Defaults to 6 seconds. - A backoff strategy: linear or exponential. Defaults to linear. -### Upgrading to 3.4.10+ +### Upgrading to 3.4.10 In Android apps with `minSdkVersion` 23 or higher ([Android 6.0](https://developer.android.com/studio/releases/platforms#6.0)) Iterable's Android SDK now encrypts the following fields when storing them at @@ -673,9 +1024,9 @@ encrypt data): - Add `multiDexEnabled true` to the `default` object, under `android`. - Add `implementation androidx.multidex:multidex:2.0.1` to the `dependencies`. -### Upgrading to 3.4.0+ +### Upgrading to 3.4.0 -- Starting with version [`3.4.0`](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) +- Starting with version [3.4.0](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.4.0) of Iterable's Android SDK, you'll need to declare the URL protocols that the SDK should expect to see on incoming links (and then handle as needed). For more information, read about [Step 5.3: Set allowed URL protocols](#step-5-3-set-allowed-url-protocols), @@ -686,7 +1037,7 @@ encrypt data): need to first grab an instance of the `IterableApi` class by calling `IterableApi.getInstance()`. For example, `IterableApi.getInstance().handleAppLink(...)`. -### Upgrading to 3.3.1+ +### Upgrading to 3.3.1 To resolve a breaking change introduced in Firebase Cloud Messaging [version 22.0.0](https://firebase.google.com/support/release-notes/android#messaging_v22-0-0), @@ -708,9 +1059,9 @@ android { } ``` -### Upgrading to 3.2.0+ +### Upgrading to 3.2.0 -[Versions 3.2.0+ of the SDK](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) +[Versions 3.2.0 and higher](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.2.0) depend on the [AndroidX](https://developer.android.com/jetpack/androidx) support libraries. To use these versions, you'll need to [migrate your app to use AndroidX](https://developer.android.com/jetpack/androidx/migrate). diff --git a/sources/android/customizing-mobile-inbox-on-android.md b/sources/android/customizing-mobile-inbox-on-android.md index abd8942..403e3f4 100644 --- a/sources/android/customizing-mobile-inbox-on-android.md +++ b/sources/android/customizing-mobile-inbox-on-android.md @@ -1,12 +1,12 @@ --- -url: https://iterable.zendesk.com/hc/articles/360039189931 +url: https://support.iterable.com/hc/articles/360039189931 title: Customizing Mobile Inbox on Android useInNovaDocs: true source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/customizing-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 502be69e2212a0540bcaaad80299d4e3936bb656 -fetched_at: 2026-05-25T15:11:42.777Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: e156e9f1bf13e4b41507a53dc10093422c8aefac +fetched_at: 2026-08-03T20:41:29.489Z --- # Customizing Mobile Inbox on Android @@ -96,7 +96,7 @@ default) or as standalone activities. To change this setting, either: - Set an extra for the activity's intent: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -104,7 +104,7 @@ default) or as standalone activities. To change this setting, either: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -114,13 +114,13 @@ default) or as standalone activities. To change this setting, either: - Pass constructor parameters to the fragment: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY, 0) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.ACTIVITY) @@ -131,7 +131,7 @@ default) or as standalone activities. To change this setting, either: When launching the mobile inbox as an activity, change the title by passing an `activityTitle` argument in the intent: -Kotlin: +_Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -139,7 +139,7 @@ intent.putExtra("activityTitle", "My Inbox") startActivity(intent) ``` -Java: +_Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -147,7 +147,80 @@ intent.putExtra("activityTitle", "My Inbox"); startActivity(intent); ``` -### Cell layout, colors and font +### Inbox toolbar (SDK v3.9.0 and above) + +Starting with SDK version 3.9.0, you can display an optional toolbar above the +inbox list using `IterableInboxToolbarView`. The toolbar is off by default, +so the inbox behaves exactly as it did in previous SDK versions unless you opt +in. + +Configure the toolbar with the `InboxToolbarOption` sealed interface, which has +these options: + +- `None` (default) — No toolbar. +- `Default` — A title-only toolbar above the inbox list. +- `WithBackButton` — A title plus a back-navigation icon. By default, the back + action calls `OnBackPressedDispatcher`. To customize it, have your host + `Activity` or parent `Fragment` implement `IterableInboxToolbarBackListener`. +- `Custom(layoutRes)` — Inflates your own toolbar layout. To wire your layout to + the SDK, tag views with these reserved IDs (both are optional): + - `@id/iterable_reserved_inbox_toolbar_action` — Automatically wired to the + SDK's back handler. + - `@id/iterable_reserved_inbox_toolbar_title` — Automatically bound to the + toolbar title. + +:::warning IMPORTANT +When the toolbar is enabled, the host activity must use a `Theme.AppCompat` +descendant. +::: + +#### Configure the toolbar on the fragment + +Pass an `InboxToolbarOption` (and, optionally, a title) to +`IterableInboxFragment.newInstance(...)`: + +_Kotlin_ + +```kotlin +val inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton, + "My Inbox" +) +``` + +_Java_ + +```java +IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance( + InboxToolbarOption.WithBackButton.INSTANCE, + "My Inbox" +); +``` + +#### Configure the toolbar on the activity + +When launching the inbox as an activity, set the `TOOLBAR_OPTION` and +`TOOLBAR_TITLE` intent extras: + +_Kotlin_ + +```kotlin +val intent = Intent(context, IterableInboxActivity::class.java) +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton) +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox") +startActivity(intent) +``` + +_Java_ + +```java +Intent intent = new Intent(getContext(), IterableInboxActivity.class); +intent.putExtra(IterableInboxFragment.TOOLBAR_OPTION, InboxToolbarOption.WithBackButton.INSTANCE); +intent.putExtra(IterableInboxFragment.TOOLBAR_TITLE, "My Inbox"); +startActivity(intent); +``` + +### Cell layout, colors, and font ![Inbox cells with a custom layout](https://iterable.zendesk.com/hc/article_attachments/360050615411/custom-layout.png "Inbox cells with a custom layout") @@ -167,7 +240,7 @@ To modify the font, color or layout of inbox cells: 3. Specify this layout ID when launching the activity: - Kotlin: + _Kotlin_ ```kotlin val intent = Intent(context, IterableInboxActivity::class.java) @@ -175,7 +248,7 @@ To modify the font, color or layout of inbox cells: startActivity(intent) ``` - Java: + _Java_ ```java Intent intent = new Intent(getContext(), IterableInboxActivity.class); @@ -185,13 +258,13 @@ To modify the font, color or layout of inbox cells: 4. Alternatively, create the fragment with custom parameters: - Kotlin: + _Kotlin_ ```kotlin val inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item) ``` - Java: + _Java_ ```java IterableInboxFragment inboxFragment = IterableInboxFragment.newInstance(InboxMode.POPUP, R.layout.custom_inbox_item); @@ -211,7 +284,7 @@ subclass `IterableInboxFragment` and set a date mapper in `onCreate`. The date mapper takes an `IterableInAppMessage` and returns a string representing the creation date of the message. If the date field should be blank, return `null`. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxDateMapperFragment : IterableInboxFragment() { @@ -229,7 +302,7 @@ class CustomInboxDateMapperFragment : IterableInboxFragment() { } ``` -Java: +_Java_ ```java public class CustomInboxDateMapperJavaFragment extends IterableInboxFragment implements IterableInboxDateMapper { @@ -269,7 +342,7 @@ the message, `false` otherwise. `IterableInboxFilter` is an interface that declares a filter method. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFilterFragment : IterableInboxFragment() { @@ -295,7 +368,7 @@ class CustomInboxFilterFragment : IterableInboxFragment(), IterableInboxFilter { } ``` -Java: +_Java_ ```java public class CustomInboxFilterFragment extends IterableInboxFragment implements IterableInboxFilter { @@ -331,7 +404,7 @@ set a comparator in `onCreate`. `IterableInboxComparator` is a standard Java `Comparator` interface: return a negative integer, zero, or a positive integer when the first message is less than, equal to, or greater than the second. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxComparatorFragment : IterableInboxFragment() { @@ -437,7 +510,7 @@ To display different inbox items with different interfaces, follow these steps: 5. The same constants will then be passed to `getLayoutForViewType`. Use them to return different layouts based on the view type. -Kotlin: +_Kotlin_ ```kotlin class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterExtension { @@ -490,7 +563,7 @@ class CustomInboxFieldsFragment : IterableInboxFragment(), IterableInboxAdapterE } ``` -Java: +_Java_ ```java public class CustomInboxFieldsJavaFragment extends IterableInboxFragment implements IterableInboxAdapterExtension { diff --git a/sources/android/embedded-messages-with-iterables-android-sdk.md b/sources/android/embedded-messages-with-iterables-android-sdk.md index a309cf3..500e2a7 100644 --- a/sources/android/embedded-messages-with-iterables-android-sdk.md +++ b/sources/android/embedded-messages-with-iterables-android-sdk.md @@ -4,9 +4,9 @@ title: Embedded Messages with Iterable's Android SDK useInNovaDocs: true source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/embedded-messaging/embedded-messages-with-iterables-android-sdk/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 6a6d6b5baf76973d611fb7f17785d3b04feeace0 -fetched_at: 2026-05-25T15:11:44.823Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 576150056520b366d5190411e9f28198e70bbeaf +fetched_at: 2026-08-03T20:41:31.068Z --- # Embedded Messages with Iterable's Android SDK @@ -457,19 +457,45 @@ to declare the styles you'd like the view to use: ```kotlin // Grab your app's colors from wherever it makes sense. val config = IterableEmbeddedViewConfig( - backgroundColor: Color.parseColor("#FFFFFF"), - borderColor: Color.parseColor("#000000"), - borderWidth: 1, - borderCornerRadius: 8f, - primaryBtnBackgroundColor: Color.parseColor("#0000FF"), - primaryBtnTextColor: Color.parseColor("#FFFFFF"), - secondaryBtnBackgroundColor: Color.parseColor("#FFFFFF"), - secondaryBtnTextColor: Color.parseColor("#000000"), - titleTextColor: Color.parseColor("#000000"), - bodyTextColor: Color.parseColor("#000000") + backgroundColor = Color.parseColor("#FFFFFF"), + borderColor = Color.parseColor("#000000"), + borderWidth = 1, + borderCornerRadius = 8f, + primaryBtnBackgroundColor = Color.parseColor("#0000FF"), + primaryBtnTextColor = Color.parseColor("#FFFFFF"), + secondaryBtnBackgroundColor = Color.parseColor("#FFFFFF"), + secondaryBtnTextColor = Color.parseColor("#000000"), + titleTextColor = Color.parseColor("#000000"), + bodyTextColor = Color.parseColor("#000000"), + imageScaleType = ImageView.ScaleType.CENTER_CROP ) ``` +:::tip TIP — Default values (SDK v3.8.0 and above) +Starting with SDK version 3.8.0, all `IterableEmbeddedViewConfig` parameters +have default values, so you only need to specify the styling options you want +to customize. The example above shows every option for reference, but you can pass +just the ones you need. For example: + +```kotlin +val config = IterableEmbeddedViewConfig( + backgroundColor = Color.parseColor("#FFFFFF"), + borderCornerRadius = 8f +) +``` + +All color, border, and text-color parameters default to `null` (which falls +back to the view's built-in styling). The `imageScaleType` parameter defaults +to `ImageView.ScaleType.CENTER_CROP`. +::: + +The `imageScaleType` parameter (added in SDK v3.8.0) controls how the image is +scaled within the 16:9 image container of `CARD` and `BANNER` views. It accepts +any standard Android [`ImageView.ScaleType`](https://developer.android.com/reference/android/widget/ImageView.ScaleType) +value (for example, `CENTER_CROP`, `FIT_CENTER`, or `FIT_XY`). The +`NOTIFICATION` view type does not display an image, so this parameter has no +effect on that view type. + Then, when it's time to display a message, create the `IterableEmbeddedView` using the `newInstance` factory method: diff --git a/sources/android/in-app-messages-on-android.md b/sources/android/in-app-messages-on-android.md index 5702459..ef33f54 100644 --- a/sources/android/in-app-messages-on-android.md +++ b/sources/android/in-app-messages-on-android.md @@ -4,9 +4,9 @@ title: In-App Messages on Android useInNovaDocs: true source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/in-app-messages-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 67ade49d8716b4d65cdb7779eb210afa0b5054da -fetched_at: 2026-05-27T12:44:52.898Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 65412ae773eaca59531243ff4775fd4457b5b608 +fetched_at: 2026-08-03T20:41:28.539Z --- # In-App Messages on Android @@ -61,6 +61,37 @@ IterableConfig config = new IterableConfig.Builder() IterableApi.initialize(context, "", config); ``` +### Deferring an in-app message (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, `onNewInApp` can also return +`InAppResponse.DEFER`. Unlike `SKIP`, which permanently drops the message, +`DEFER` keeps the message pending so the SDK reconsiders it on a later display +pass (for example, on the next foreground, sync, or newly arrived message). This +is useful for temporary, per-message suppression—for example, while a splash +screen is showing. + +```java +class MyInAppHandler implements IterableInAppHandler { + @Override + public InAppResponse onNewInApp(IterableInAppMessage message) { + if (appIsShowingSplashScreen()) { + return InAppResponse.DEFER; + } + return InAppResponse.SHOW; + } +} +``` + +Once your app is ready to display in-app messages, call +`resumeInAppDisplay()` (see [Pausing the display of in-app messages](#pausing-the-display-of-in-app-messages-sdk-v3-2-6-and-above)) +to re-check pending messages immediately, instead of waiting for the next +foreground or sync trigger. + +:::info NOTE +In Kotlin, add a `DEFER` branch to any exhaustive `when` expression over +`InAppResponse`. +::: + ## Getting the local queue of in-app messages The SDK keeps the local in-app message queue in sync by checking the server queue @@ -109,6 +140,71 @@ The SDK handles in-app message buttons and links as follows: returns `false` for the provided URL, the URL will be opened by the system (using a web browser or other application, as applicable). +## Configuring how in-app messages interact with system bars (SDK v3.8.0 and above) + +By default, Iterable's Android SDK draws in-app messages edge-to-edge, with +content extending behind the status bar and navigation bar. This was the only +behavior in SDK versions 3.6.1 through 3.7.0. + +Starting with SDK version 3.8.0, you can configure how in-app messages interact +with system bars by setting `IterableInAppDisplayMode` on `IterableConfig`. +This setting applies globally to all in-app messages displayed by the SDK. + +The available modes are: + +- `FORCE_EDGE_TO_EDGE` (default) — Forces in-app messages to display + edge-to-edge, drawing content behind the status bar and navigation bar. + This preserves the behavior of previous SDK versions. +- `FOLLOW_APP_LAYOUT` — Matches the host app's current layout configuration. + If your app is edge-to-edge, in-app messages display edge-to-edge; if your + app respects system bar bounds, so do in-app messages. +- `FORCE_FULLSCREEN` — Hides the status bar entirely while in-app messages + are displayed. Uses the legacy `FLAG_FULLSCREEN` on API levels below 30 and + `WindowInsetsController` on API 30 and above. +- `FORCE_RESPECT_BOUNDS` — Ensures in-app content never draws behind the + status bar or navigation bar, keeping UI elements like the close button + always accessible. + +To configure the display mode, call `setInAppDisplayMode()` on +`IterableConfig.Builder`: + +```java +IterableConfig config = new IterableConfig.Builder() + .setInAppDisplayMode(IterableInAppDisplayMode.FOLLOW_APP_LAYOUT) + .build(); +IterableApi.initialize(context, "", config); +``` + +:::tip TIP +If the close button (or other interactive elements) in your fullscreen in-app +messages is being obscured by the status bar, switch to `FOLLOW_APP_LAYOUT` +or `FORCE_RESPECT_BOUNDS`. +::: + +## Displaying in-app messages in Jetpack Compose apps (SDK v3.9.0 and above) + +In SDK versions before 3.9.0, displaying an in-app message required a +`FragmentActivity`, because the SDK rendered in-app messages using a `Fragment`. +This meant that apps built fully with [Jetpack Compose](https://developer.android.com/compose) +(and without the Android fragment framework) couldn't display in-app messages. + +Starting with SDK version 3.9.0, the SDK can also render in-app messages using a +`Dialog`-based renderer (`IterableInAppDialogNotification`) that doesn't require +a `FragmentActivity`. When the current activity is a `FragmentActivity`, the SDK +continues to use the existing `Fragment`-based rendering; when it isn't (for +example, a Compose-based `ComponentActivity`), the SDK falls back to the +`Dialog`-based renderer. As a result, in-app messages now display correctly in +apps built fully with Jetpack Compose. + +No code changes are required to take advantage of this—just upgrade to SDK +version 3.9.0 or later. (The host must still be an `Activity`.) + +:::warning IMPORTANT +This Compose compatibility applies to **in-app message rendering** only. Iterable's +mobile inbox UI is still fragment-based and requires a `FragmentActivity` host. For +more information, see [Setting up Mobile Inbox on Android](https://support.iterable.com/hc/articles/360038744152#displaying-the-mobile-inbox). +::: + ## Changing the display interval between in-app messages To customize the time delay between successive in-app messages, set @@ -143,6 +239,32 @@ method on `IterableInAppManager` to manually display messages. To resume the display of in-app messages from your app's queue, call `setAutoDisplayPaused(false)`. +### Re-evaluating pending in-app messages on demand (SDK v3.10.0 and above) + +Starting with SDK version 3.10.0, you can call `resumeInAppDisplay()` to prompt +the SDK to re-evaluate pending in-app messages once your app is ready to display +them—for example, after a splash screen is dismissed, or after you deferred a +message by returning `InAppResponse.DEFER` from `onNewInApp` (see +[Deferring an in-app message](#deferring-an-in-app-message-sdk-v3-10-0-and-above)). +Without this call, the SDK re-checks pending messages only on its own triggers +(foreground, sync, or a newly arrived message). + +_Kotlin_ + +```kotlin +IterableApi.getInstance().inAppManager.resumeInAppDisplay() +``` + +_Java_ + +```java +IterableApi.getInstance().getInAppManager().resumeInAppDisplay(); +``` + +`resumeInAppDisplay()` is independent of `setAutoDisplayPaused(boolean)`: if +automatic display is paused, this call won't show anything (and logs a warning) +until you also call `setAutoDisplayPaused(false)`. + ## Further reading diff --git a/sources/android/setting-up-mobile-inbox-on-android.md b/sources/android/setting-up-mobile-inbox-on-android.md index 60f6f6a..4065988 100644 --- a/sources/android/setting-up-mobile-inbox-on-android.md +++ b/sources/android/setting-up-mobile-inbox-on-android.md @@ -4,9 +4,9 @@ title: Setting up Mobile Inbox on Android useInNovaDocs: true source_repo: Iterable/iterable-docs source_path: docs/developer-and-api-docs/in-app-messages/setting-up-mobile-inbox-on-android/index.md -source_ref: 16ae7f4a908f84d6eb15fe6f5390f07cc5afe20d -source_sha: 968c6cc97c2aec2e8e4124b5ef18aa9a000a139d -fetched_at: 2026-05-25T15:11:41.984Z +source_ref: 59c40504c91bc0b13751c5ef5f348810eb0fd4f2 +source_sha: 9b54bece973b76efd0e1eaec0494b0e9d2c2af7c +fetched_at: 2026-08-03T20:41:29.000Z --- # Setting up Mobile Inbox on Android @@ -38,6 +38,21 @@ To add a mobile inbox to your Android app, first install Iterable's ## Displaying the mobile inbox +:::warning IMPORTANT +Iterable's mobile inbox UI is fragment-based: `IterableInboxFragment` requires a +`FragmentManager`, so its host must be a `FragmentActivity` (or its descendant, +`AppCompatActivity`). Compose-first apps often use a plain `ComponentActivity` as +their host, which has no `FragmentManager`—hosting the inbox fragment there +crashes when the fragment is attached. If your app is Compose-first, change the +host activity's base class to `FragmentActivity` / `AppCompatActivity` before +adding the inbox. (Iterable's Android SDK doesn't currently provide a +Compose-native inbox.) + +Note that this requirement applies to the inbox UI only. Starting with SDK +version 3.9.0, in-app messages themselves render correctly in Compose-first +apps. For more information, see [In-App Messages on Android](https://support.iterable.com/hc/articles/360035537231#displaying-in-app-messages-in-jetpack-compose-apps-sdk-v3-9-0-and-above). +::: + In your app, show the mobile inbox when the user selects a specific tab or taps a particular button. @@ -58,13 +73,13 @@ a particular button. Use the provided `InboxActivity` wrapper: - Kotlin: + _Kotlin_ ```kotlin startActivity(Intent(context, IterableInboxActivity::class.java)) ``` - Java: + _Java_ ```java startActivity(new Intent(getContext(), IterableInboxActivity.class)); @@ -91,7 +106,7 @@ For more information about cross-device read state syncing, see: ## Customizing the mobile inbox To learn how to customize the mobile inbox in an Android app, read -[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039091471). +[Customizing Mobile Inbox on Android](https://support.iterable.com/hc/articles/360039189931). ## Further reading