Android Low-Code Integration

Use the LowCodeMobile Android SDK to display personalized content in your Android app with minimal setup. The SDK renders out-of-the-box Banner and Recommendations components and supports custom components for fully custom UI.

  1. Install the SDK
  2. Initialize the SDK
  3. Display a Content Zone
  4. Out-of-the-Box Components
  5. Custom Components
  6. Engagement Tracking
  7. Preview
  8. Design-Time Rendering
  9. Error Handling

Add the required Maven repositories to your project-level settings.gradle.kts (or build.gradle):

Add the LowCodeMobile dependencies to your app-level build.gradle.kts:

If you are using the out-of-the-box components (the SDK and lowcodemobile dependencies are pulled in automatically):

If you are not using the out-of-the-box components (the SDK dependency is pulled in automatically):

Your app’s SDK levels must meet or exceed the following minimums.

SettingRequired
minSdk26 (Android 8.0)
compileSdk34 (Android 14)
Kotlin2.4+

The host app owns SDK initialization. Initialize both the Data 360 module and Personalization modules in your Application.onCreate(). The SDK queues any content zone requests made before initialization completes, so you do not need to gate your UI on init status.

In your production application, you must explicitly manage user consent using the solution provided in the Engagement Mobile SDK. See Data 360 Consent Management.

  • Replace "<Your CDP App ID>", "<Your CDP Endpoint>", and "<Your CDN URL>" with the values provided by your Salesforce Marketing Cloud administrator.
  • To avoid committing credentials, read these values from AndroidManifest.xml <meta-data> entries or a build configuration.
  • To enable debug logging during development, add SFMCSdk.setLogging(LogLevel.DEBUG, AndroidLogger()) before SFMCSdk.configure(...), so logging is active for the whole init sequence. Remove this before building for production.

Once the SDK is initialized, use the ContentZone composable in Jetpack Compose. Pass the content zone identifier and a list of components the zone is allowed to render. The SDK fetches a decision from the backend, matches the component name returned, and renders the matching component.

After adding a ContentZone to your app code, create the matching content zone record in Salesforce Personalization. See Set Up Mobile Content Zones for instructions on defining personalization points, assigning components, and configuring engagement definitions.

ContentZone is a standard composable - place it anywhere in your layout alongside other content. The Pull-to-refresh example below shows it embedded within a scrollable screen.

The out-of-the-box component names registered internally are "Salesforce_Banner" and "Salesforce_Recommendations".

ParameterRequiredDescription
nameYesThe content zone identifier, matching your backend configuration (case-sensitive).
allowedComponentsYesThe Components the zone may render. If the backend returns a name not in this list, the fallback is shown.
decisionsRequestContextNoOptional context to bias personalization decisions.
timeoutMsNoFetch timeout in milliseconds. Default: 10,000 (10 seconds).
controllerNoContentZoneController for programmatic refresh.
loadingNoComposable shown while content is loading.
fallbackNoComposable shown when content cannot be loaded or no component matches.

Use a ContentZoneController to refresh the zone programmatically:

The SDK ships with two pre-built components - SalesforceBanner and SalesforceRecommendations - that handle layout, styling, and configuration-driven engagement tracking out of the box. Use these when you want to display personalized content without writing a custom UI. Both support an onTap callback for custom tap handling; by default, tapping opens the ctaUrl if one is provided by the backend.

These components are available out-of-the-box in the UI and are registered under the names "Salesforce_Banner" and "Salesforce_Recommendations".

Pass a DecisionsRequestContext to bias recommendations based on what the user is currently viewing. All fields are optional - if you only supply anchorId, the system infers the type automatically.

Override the default appearance of out-of-the-box components by passing a style object:

Implement the Component<Model> interface (package com.salesforce.personalization.lowcodemobile) to render a content zone with your own UI. Each component defines:

  • val name: String - The component name that matches the backend experience template’s component name.
  • val modelClass: KClass<Model> - The Kotlin class reference for the component model.
  • validateAndCreateComponentModel(unvalidatedJson, componentContext) - Validates the JSON payload and returns a Result.Success(model) or Result.Failure(exception).
  • @Composable Compose(model, componentContext) - Renders the composable UI for the component.

Define a custom component with its own component name, model, and associated experience template in the backend. Give the component a unique name (for example, CustomHero), then create a matching experience template in the Core UI and a matching custom component in your app. Your model’s fields and types must match the experience template’s schema, though you don’t need to use all of them.

For a component that renders a list (like SalesforceRecommendations), use the per-item variants of the engagement functions to report engagement for each item in the list.

Before engagement events are recorded, ensure your engagement definitions (View, Click) are configured in the Data 360 mobile connector. See Set Up Mobile Engagement Tracking for instructions on defining engagement actions alongside your components and content zones.

Out-of-the-box components (SalesforceBanner, SalesforceRecommendations) track View and Click engagement automatically. These are the only two actions currently supported end-to-end. The SDK accepts custom action strings for future expansion.

For custom components, engagement is not automatic - you must call these methods explicitly from your component code:

FunctionUse for
componentContext.trackEngagement(action)Clicks on a single-item component (for example, CustomHero)
componentContext.trackEngagementPerItem(index, action)Clicks on an item within a multi-item component (for example, CustomItemList)
TrackEngagementViewOnce(componentContext)Views of a single-item component - a Composable, call once per personalization
TrackEngagementViewOncePerItem(componentContext, index)Views of an item within a multi-item component - a Composable, call once per item per personalization

See Define a custom component and Define a multi-item custom component above for both in use.

The SDK supports previewing personalized content via QR code or preview URL. When a URL containing the sfp-preview parameter is opened, the SDK renders preview content in all active content zones.

Before using preview, make sure:

  1. Your app is configured to handle URL schemes or app links (via AndroidManifest.xml intent filters).
  2. The base URL is set in your Data 360 mobile connector configuration.

Use MockDataContentZone to render components with mock data for styling and layout without any backend setup or networking. This is not related to QR-code based previewing - it simply lets you see how components look during development.

To try out your fallback composable, pass a Result.Failure:

The SDK uses PersonalizationException with a Type enum for error reporting:

TypeDescription
UNKNOWNAn unknown error occurred.
INITIALIZATIONThe SDK has not been initialized or failed to initialize.
CONSENTUser consent is not set to opt-in.
REQUEST_INVALIDThe request parameters are invalid.
NETWORKA network error occurred during the fetch.
RESPONSE_INVALIDThe server response could not be parsed.
TIMEOUTThe fetch request exceeded the timeout duration.

ContentZone handles errors internally. If no fallback is provided, the zone logs the error and shows nothing - this is the typical production behavior. If you want to display fallback content, pass a fallback composable. The following example is for illustration only - consider what behavior is appropriate for your production app: