Skip to main content
Supported on: Android SDK 1.0.33+
The AppDNA Android SDK supports rich media content across onboarding flows, paywalls, in-app messages, and surveys. All rich media is configured in the AppDNA Console — no code changes are needed to add animations, videos, or effects to your flows.

Supported Formats

Lottie Animations

Lottie animations are rendered natively using Lottie Compose, which is bundled with the SDK.
An animation comes from a URL, and its colours are baked into the file. There is no inline animation payload and no colour remapping: both were declared in the config models for a while, neither renderer ever read them, and they were removed rather than implemented. A remote .json or .lottie keeps the config a device downloads small, and a recolour belongs in the file the designer ships, where it can be seen before it is published.
Lottie is supported in onboarding content blocks, paywall sections, in-app message hero areas, and survey thank-you screens.

Rive Animations

Rive animations are rendered natively using the Rive Android runtime, bundled with the SDK. Rive is supported in onboarding content blocks, paywall sections, in-app messages (both modal and fullscreen) and server-driven screens — everywhere Lottie is except surveys.

Video

Inline video playback with ExoPlayer:
  • Remote URL — provide a URL to an MP4 or HLS video
  • Thumbnail — optional poster image shown before playback
  • Controls — autoplay, loop, muted, show/hide controls
  • Sizing — configurable height and corner radius
Video is supported in onboarding content blocks, paywall sections, and in-app message hero areas.

Visual Effects

Haptic Feedback

Haptic feedback uses the Android HapticFeedbackConstants and VibrationEffect APIs. Supported haptic types: 🔴 Nothing fires unless haptic.enabled is true. The config object gates every trigger, so a trigger set on its own does nothing — triggerIfEnabled returns early when the config is absent or not enabled, on both platforms. A type the SDK does not recognise is also ignored rather than falling back to a default. The triggers, each taking one of the types above:

Particle Effects

Particle effects overlay the current screen using Jetpack Compose Canvas. Supported effect types: 🔴 The surface decides when the trigger’s moment is, and each surface has only the moments it can actually hold. An effect whose trigger is not one of its surface’s moments has nothing to fire in, and nothing is drawn. 🔴 on_flow_complete is not an onboarding moment. The host takes the flow off the screen as soon as it completes, so an effect armed there would be dismissed in the frame it started. A celebration at the end of a flow belongs on whatever screen the app shows next.
Fixed in iOS 1.0.84 / Android 1.0.56. Before those versions only on_appear ever fired: the overlay re-checked the authored trigger after its parent had already decided to show it, and refused anything else. A paywall effect left on its Android default — on_purchase — therefore never ran at all.

Blur / Glassmorphism

Blur backdrops use RenderEffect.createBlurEffect() (API 31+) with graceful fallback on older devices:
  • Radius — blur intensity
  • Tint — overlay color tint
  • Saturation — color saturation adjustment
Blur is supported as a backdrop for in-app message modals and fullscreen overlays.

Asset URL Conventions

Each rich-media block carries its own typed URL field. The SDK picks the renderer from the block’s type, not from the file extension, so the field name is what matters: 🔴 A screen SECTION spells the poster thumbnail_url, without the video_ prefix, because the screens editor writes it that way. Content blocks and paywall sections use the prefixed name. Both renderers accept either spelling on either surface, so the prefixed form is always safe.

Embedding Rich Media in Paywalls

Rich media inside a paywall belongs to a section. A section can host an image, a Lottie or Rive animation, or a video, and the SDK draws it when the paywall is presented — there is nothing to forward from your code:
Common patterns:
  • Hero animation — a looping Lottie at the top of the paywall while the user reads the features.
  • Feature illustrations — each feature row can carry a Material icon, an emoji or a remote image.
  • Video proof — a short looping product demo in its own section.
  • CTA animation — pulse, glow and bounce are configured on the CTA section itself.

Embedding Rich Media in Onboarding

An onboarding step is an ordered list of content blocks. The rich-media types are image, video, lottie and rive; they sit alongside heading, text, button, list, badge, icon, toggle, divider and spacer in the same list and are laid out in order.

Embedding Rich Media in In-App Messages

A modal, banner or fullscreen message has a hero area at the top, which can host an image (animated GIFs included), a Lottie animation or a short looping video. The body below the hero uses the same content-block system as onboarding. Messages are triggered by event rules in the Console. Implement AppDNAInAppMessageDelegate to observe them, or to suppress one:

Animation Playback Control

Playback is authored per block in the Console; the renderer reads the fields off the published document and there is nothing to forward through your own code. 🔴 Video fields are video_-prefixed and Lottie’s are not. autoplay and loop belong to Lottie; the video block’s equivalents are video_autoplay and video_loop. And Lottie’s speed is lottie_speed, not speed — a bare speed is a different field entirely (the particle background’s slow / normal / fast token), so authoring speed on a Lottie block does nothing. Android reads the prefixed video names only. iOS also decodes the unprefixed autoplay / loop / muted / controls for the video block as a legacy fallback, so a document written against iOS alone can carry names Android ignores — prefer the prefixed names.

Local Asset Embedding

scripts/bundle-assets.ts in the AppDNA repo walks a downloaded config bundle, fetches the images it references into an appdna-assets/ folder, and rewrites each URL to the relative path appdna-assets/<hash><ext>. Put that folder in app/src/main/assets/ and the SDK’s image loader resolves each path out of the app’s own assets instead of the network. 🔴 A path the build did not bundle falls through to the remote URL. The asset is opened before the path is rewritten, so a partially bundled config degrades to the network rather than to broken images — and a config you have not bundled at all is unaffected. What it does not cover: images only. The resolver loads an image, so a rewritten .json, .lottie, .riv, .mp4 or .mov path resolves to nothing on any platform, and a config containing those should not be bundled — the remote URL that worked before bundling would be replaced by a relative path that does not. There is no asset:// URL form.

Performance Notes

  • Lazy loading — a rich-media block starts loading when its composable first enters composition, so an asset on a later onboarding step is not fetched while the user is on the first.
  • Image caching — images load through a Coil ImageLoader the SDK configures with a 50 MB disk cache and a memory cache of 25% of the app’s memory budget.
  • Memory — players and animations are released when the composable leaves composition, so they do not accumulate across flows.
  • Cold start — avoid a large video on the very first onboarding step; use an image there and put video on a later step so playback starts after the config has settled.
  • File size — the config a device downloads carries the URL, not the animation, so a large .lottie costs a fetch rather than a bigger config. Keep it under a few hundred KB anyway: it is fetched on the step that draws it.

Icons

The SDK supports structured icon references across all modules. Icons are resolved to platform-appropriate rendering: Icons are used in onboarding content blocks, paywall feature lists, in-app message CTA buttons, and survey question options.

Content Blocks

Onboarding steps support rich content blocks that are rendered in order using Jetpack Compose. Each block has a type and optional styling:

Styling

All visual elements support fine-grained styling through the Console:
  • Text styles — font family, size, weight, color, alignment, line height, letter spacing, opacity
  • Backgrounds — solid color, linear/radial gradient, or remote image with overlay
  • Borders — width, color, style, and per-corner radius
  • Shadows — offset, blur, spread, and color
  • Spacing — per-side padding and margin
  • Animations — entry animations (slide, fade, scale), section stagger, CTA pulse/glow/bounce, dismiss animations
The SDK uses platform-native fonts only. Custom web fonts are not supported. Specify system font families in the Console, and the SDK will render them using the device’s default typeface.

Dynamic Content Templates

Rich media content supports template interpolation using {{variable}} syntax. Available namespaces: Use the pipe syntax for fallback values: {{user.name | there}} renders as “there” if the user name is not set or was identified as null.

No Code Required

All rich media features are configured in the AppDNA Console. The SDK renders them automatically based on the config bundle. You do not need to write any code to use Lottie, Rive, video, haptics, particles, blur, icons, or content blocks.