Readme · Guides · Reference · Contributing
The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.
This is the native Android reference implementation for the
Contentful Optimization Android SDK. It demonstrates the
integration pattern for both Jetpack Compose (:compose) and XML Views (:views), and is the
target for the shared Maestro E2E suite (see
maestro/README.md).
OptimizationRoot initialization with mock server configurationOptimizedEntry personalization with view and click trackingScreenTrackingEffectPreviewPanelConfig preview panel with audience/variant override controlsThe app defines one locale in shared config, passes it to the native SDK as top-level locale, and
passes it directly to the raw CDA fetch helper. Entries passed to OptimizedEntry use the standard
single-locale CDA entry shape. Do not use all-locale CDA responses or locale=*, because SDK entry
resolution expects direct single-locale fields such as fields.nt_experiences and
fields.nt_variants. See
Locale handling in the Optimization SDK Suite
for the broader locale model and
Entry personalization and variant resolution
for the entry contract.
This mock app uses one Contentful locale. A production app can derive the application locale from
its own navigation, i18n, or account-preference layer and pass that value to both SDK locale and
CDA requests when they should stay aligned.
ANDROID_HOME setadb in PATHpnpm install)pnpm --filter @contentful/optimization-js-bridge buildFrom the monorepo root:
pnpm install
pnpm --filter @contentful/optimization-js-bridge build
This implementation does not use a local .env file. Mock API settings live in the Android app
configuration and point emulator traffic to the host mock server through http://10.0.2.2:8000.
The bootstrap script starts the mock server, builds the app, and launches it on an emulator:
cd implementations/android-sdk
./scripts/bootstrap.sh
Or manually:
# Terminal 1: Start mock server
pnpm serve:mocks
# Terminal 2: Build and install (the app reaches the host mock via 10.0.2.2 — no adb reverse needed)
cd implementations/android-sdk
./gradlew :compose:assembleDebug
adb install -r compose/build/outputs/apk/debug/compose-debug.apk
adb shell am start -n com.contentful.optimization.app/.MainActivity
To launch with a clean SDK state (clears the persisted profile on cold start):
adb shell am start -n com.contentful.optimization.app/.MainActivity --ez reset true
The E2E suite uses Maestro. Prefer the monorepo-root wrappers or local runner; they start the mock server, resolve or launch an emulator, build/install the app, and run the flow suite.
Run both Compose and XML Views apps:
pnpm implementation:run -- android-sdk test:e2e
Run one app shell:
pnpm implementation:run -- android-sdk test:e2e:compose
pnpm implementation:run -- android-sdk test:e2e:views
Run one Maestro suite:
pnpm implementation:run -- android-sdk test:e2e:compose -- --flow preview-panel
From implementations/android-sdk/, the equivalent Compose-only local runner is:
APP_PACKAGE=com.contentful.optimization.app ./scripts/run-e2e.sh --flow preview-panel
Omit APP_PACKAGE to run the same suite against both Compose and XML Views.
See scripts/README.md for emulator, AVD, and environment-variable details.
Open this directory (implementations/android-sdk/) as an Android Studio project. After Gradle
sync, build and launch either app on the selected device (MainActivity in :compose or :views),
set breakpoints in the app or SDK source, and run the JVM unit tests from the gutter.
Before running the app from the IDE, in a separate terminal:
# From the monorepo root, build the bridge once (or after bridge source changes):
pnpm --filter @contentful/optimization-js-bridge build
# Then start the mock server and leave it running:
pnpm --dir lib/mocks serve
The E2E suite is run from the command line rather than an IDE run configuration; see
maestro/README.md for flow structure.
Use this app when you need a debuggable native Android surface for changes in
packages/android/ContentfulOptimization or the shared JS bridge. The Gradle project includes the
SDK module from the workspace as an included Gradle subproject, so app builds compile the Kotlin
source and package the local bridge asset rather than a published AAR.
The normal loop is:
Edit Kotlin in packages/android/ContentfulOptimization/src/main/kotlin/... or bridge TypeScript
in packages/universal/optimization-js-bridge/src/....
Build the changed app or both app shells from implementations/android-sdk/:
./gradlew :compose:assembleDebug :views:assembleDebug
Run the Compose or Views app locally, then validate with the matching Maestro flow.
If bridge source changed, rebuild the bridge before treating app results as meaningful:
pnpm --filter @contentful/optimization-js-bridge build
Run the smallest check that covers the changed surface:
| Change area | Suggested validation |
|---|---|
| Bridge TypeScript only | pnpm --filter @contentful/optimization-js-bridge typecheck and pnpm --filter @contentful/optimization-js-bridge build |
| Kotlin SDK or UI adapter behavior | ./gradlew :compose:assembleDebug :views:assembleDebug |
| Compose or Views user flow | pnpm implementation:run -- android-sdk test:e2e:compose -- --flow <suite> or pnpm implementation:run -- android-sdk test:e2e:views -- --flow <suite> |
| Shared preview-panel behavior | Run the affected Maestro suite against both apps |
| Documentation-only README changes | Prettier on touched Markdown and git diff --check |
Common local pitfalls:
http://10.0.2.2:8000; no manual adb reverse setup is
required for normal local runs.