Skip to main content

Background

Get set up with the Helium SDK for Android. Reach out over your Helium slack channel or email founders@tryhelium.com for any questions.

Installation

Add the Helium SDK to your project using Gradle.

Requirements

  • Kotlin Version: 2.0.0 or higher
  • Java Version: 8 or higher
  • Minimum Android SDK: 23 or higher
  • Compile Android SDK: 35 or higher

1. Add repositories to your settings.gradle.kts file:

Ensure you have mavenCentral() and google() in your repositories blocks.
// settings.gradle.kts

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // You might have other repositories here
    }
}
If you don’t have a dependencyResolutionManagement block, ensure google() and mavenCentral() are present in your pluginManagement { repositories { ... } } block.

2. Add the dependencies to your module-level build.gradle.kts file (e.g., app/build.gradle.kts):

  • Core (Android View System)
  • Jetpack Compose UI
  • RevenueCat
// app/build.gradle.kts

dependencies {
    // Choose one of the UI modules:
    implementation("com.tryhelium.paywall:core:0.1.11")
    // Other app dependencies
}

Initialize Helium

You need to initialize Helium before you can present a paywall. The best place to do this is in your MainActivity’s onCreate() method.
// In your MainActivity class's onCreate() method
import com.tryhelium.paywall.core.HeliumEnvironment

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val heliumPaywallDelegate = PlayStorePaywallDelegate(this) // Or your custom delegate

        Helium.initialize(
            context = this,
            apiKey = "YOUR_API_KEY",
            heliumPaywallDelegate = heliumPaywallDelegate,
            environment = HeliumEnvironment.SANDBOX // Or HeliumEnvironment.PRODUCTION
        )
    }
}
Helium.initialize
method
In most cases there is no need to check download status. Helium will display a loading indication if a paywall is presented before download has completed.
The downloadStatus is a Kotlin Flow that emits HeliumConfigStatus states. The possible states are:
  • HeliumConfigStatus.NotYetDownloaded: The initial state before the download has started.
  • HeliumConfigStatus.Downloading: Indicates that the paywall configuration is currently being downloaded.
  • HeliumConfigStatus.DownloadFailure: Indicates that the paywall configuration download has failed.
  • HeliumConfigStatus.DownloadSuccess: Indicates that the paywall configuration has been successfully downloaded.
Here’s how you can observe the downloadStatus flow in your Activity or Fragment:
// In your Activity or Fragment
lifecycleScope.launch {
    Helium.shared.downloadStatus.collect { status ->
        when (status) {
            is HeliumConfigStatus.NotYetDownloaded -> {
                // Handle not yet downloaded state
            }
            is HeliumConfigStatus.Downloading -> {
                // Handle downloading state
            }
            is HeliumConfigStatus.DownloadFailure -> {
                // Handle download failure
            }
            is HeliumConfigStatus.DownloadSuccess -> {
                // Handle download success
            }
        }
    }
}

HeliumPaywallDelegate

You can provide an implementation of the HeliumPaywallDelegate or use one of the default implementations that we have provided, such as PlayStorePaywallDelegate or RevenueCatPaywallDelegate.
  • PlayStorePaywallDelegate
  • RevenueCatPaywallDelegate
  • Custom Delegate
Use the PlayStorePaywallDelegate to handle purchases using Google Play Billing.

Presenting Paywalls

To present a Paywall, ensure you have initialized the library. The recommended and simplest way to present a paywall is by using the Helium.presentUpsell function. This function handles the creation and presentation of the paywall activity for you.
// From an Activity or Fragment
Helium.presentUpsell(
    context = this,
    trigger = "sdk_test"
)
Helium.presentUpsell
method
For more advanced use cases, such as handling results from the paywall, you can create an Intent and launch it manually. This is useful when you need to know if a purchase was successful. The first step is to create an Intent using the createPaywallIntent helper function. This function configures and returns an Intent that can be used to launch the HeliumPaywallActivity. Here are the available options for createPaywallIntent:
  • context: The Android Context required to create the intent. Use this in an Activity or LocalContext.current in a Composable.
  • trigger: The specific paywall trigger you want to display. This is a required parameter.
  • fullscreen: A Boolean to determine if the paywall should be displayed in fullscreen immersive mode, where the system bars (status and navigation) are hidden. This is optional and defaults to false.
  • disableSystemBackNavigation: A Boolean to control the system back button behavior. If true, the back button will not close the paywall. This is optional and defaults to true.
// Create the intent to launch the paywall
val heliumIntent = createPaywallIntent(
    context = this,
    trigger = "sdk_test"
)
Once you have your intent, you can launch it using either Jetpack Compose or the Android View System. We provide two different ways of presenting the Paywall with an intent:
  • Jetpack Compose
  • Android View System
The recommended way to present a paywall in Jetpack Compose is by launching the HeliumPaywallActivity using rememberLauncherForActivityResult. This approach allows you to receive a result back from the paywall, such as whether a purchase was successful.Here is an example of how to set it up in your Composable function:
val context = LocalContext.current
val paywallLauncher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // Handle successful purchase
    } else {
        // Handle cancellation
    }
}

Button(onClick = {
    paywallLauncher.launch(heliumIntent)
}) {
    Text("Show Paywall")
}
Alternatively, if you are using NavController, you can add the paywall to your navigation graph using buildHelium():
NavHost(
    navController = navController,
    startDestination = "landing",
    modifier = Modifier.fillMaxSize()
) {
    composable("landing") {
        LandingScreen(navController = navController)
    }
    buildHelium(
        navigationDispatcher = { command ->
            when (command) {
                HeliumNavigationCommand.GoBack -> navController.popBackStack()
                is HeliumNavigationCommand.Paywall -> navController.navigate("helium?trigger=${command.trigger}")
            }
        }
    )
}
To present a paywall, you can then navigate to the Helium route with a specific trigger:
// Navigate to the paywall
navController.navigate("helium?trigger=sdk_test")

PaywallEventHandlers

The Helium SDK allows you to listen for various paywall-related events. This is useful for tracking analytics, responding to user interactions, or handling the paywall lifecycle. There are two ways to listen for events: using the PaywallEventHandlers class for specific callbacks, or implementing the HeliumEventListener interface to receive all events.

Option 1: Using PaywallEventHandlers

You can create an instance of PaywallEventHandlers and provide lambdas for the events you are interested in. The available handlers are:
  • onOpen: Called when a paywall is displayed to the user.
  • onClose: Called when a paywall is closed for any reason.
  • onDismissed: Called when the user explicitly dismisses a paywall without purchasing.
  • onPurchaseSucceeded: Called when a purchase completes successfully.
  • onOpenFailed: Called when a paywall fails to open.
  • onCustomPaywallAction: Called when a custom action is triggered from the paywall.
To register your handlers, use Helium.shared.addPaywallEventListener. You can either tie the listener to a lifecycle (recommended) or manage it manually. Lifecycle-Aware (Recommended) Pass a LifecycleOwner (like an Activity or Fragment) to have the listener automatically removed when the lifecycle is destroyed.
class MyActivity : AppCompatActivity() {
    private val paywallEventHandlers = PaywallEventHandlers(
        onOpen = { event -> print("Paywall opened: ${event.paywallName}") },
        onClose = { event -> print("Paywall closed: ${event.paywallName}") }
        // ... other event handlers
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Add the listener once, it will be cleaned up automatically
        Helium.shared.addPaywallEventListener(this, paywallEventHandlers)
    }
}
Manual Management If you don’t provide a LifecycleOwner, you are responsible for removing the listener to prevent memory leaks.
// Add the listener
override fun onResume() {
    super.onResume()
    Helium.shared.addPaywallEventListener(paywallEventHandlers)
}

// Remove the listener to avoid leaks
override fun onPause() {
    super.onPause()
    Helium.shared.removeHeliumEventListener(paywallEventHandlers)
}

Option 2: Implementing HeliumEventListener

For a more centralized approach, your class can implement the HeliumEventListener interface and handle all events in a single onHeliumEvent method.
// In your Activity or Fragment
import com.tryhelium.paywall.core.event.HeliumEventListener
import com.tryhelium.paywall.core.event.HeliumEvent
import com.tryhelium.paywall.core.event.PaywallOpen
import com.tryhelium.paywall.core.event.PaywallClose

class MyActivity : AppCompatActivity(), HeliumEventListener {

    override fun onResume() {
        super.onResume()
        // Register this class as the listener
        Helium.shared.addPaywallEventListener(this, this)
    }

    override fun onPause() {
        super.onPause()
        // Unregister to prevent memory leaks
        Helium.shared.removeHeliumEventListener(this)
    }

    override fun onHeliumEvent(event: HeliumEvent) {
        when (event) {
            is PaywallOpen -> {
                // Handle paywall open
            }
            is PaywallClose -> {
                // Handle paywall close
            }
            // ... handle other event types
        }
    }
}
Usage Suggestions:
  • Use onDismiss for post-paywall navigation when the paywall is dismissed but a user’s entitlement hasn’t changed
  • Use onPurchaseSucceeded for your post purchase flow (e.g., a premium onboarding navigation)
  • Use onClose to handle a paywall close, regardless of reason

Fallbacks and Loading Budgets

If a paywall has not completed downloading when you attempt to present it, a loading state can be displayed. By default, Helium will show this loading state (a shimmer view) for up to 2 seconds (2000ms). You can configure this behavior, turn it off, or set trigger-specific loading budgets using the HeliumFallbackConfig object during initialization. If the loading budget expires before the paywall is ready, a fallback paywall will be shown if one is provided. Otherwise, the loading state will hide, and a PaywallOpenFailed event will be dispatched. There are three options for fallbacks in the Android SDK:
  • Fallback bundles: A pre-packaged paywall bundle stored in your app’s assets directory.
  • Default fallback view: A custom Android View to be used for all triggers.
  • Fallback view per trigger: A map of trigger names to specific Android Views.
All of this is configured via the HeliumFallbackConfig object passed into Helium.initialize(). Here are some examples: 1. Providing a fallback bundle: Place your fallback JSON file in the src/main/assets directory of your module. Then, initialize Helium with the fallbackBundleName.
// In your Activity's or Application's onCreate() method
Helium.initialize(
    // ... other parameters
    environment = HeliumEnvironment.SANDBOX, // Or HeliumEnvironment.PRODUCTION
    fallbackConfig = HeliumFallbackConfig.withFallbackBundle(
        fallbackBundleName = "fallback-bundle-name.json"
    )
)
2. Providing a fallback view and configuring loading budgets: You can also provide a custom View and fine-tune the.
// In your Activity's or Application's onCreate() method
val fallbackView = YourFallbackView(this) // Your custom fallback view

Helium.initialize(
    // ... other parameters
    environment = HeliumEnvironment.SANDBOX, // Or HeliumEnvironment.PRODUCTION
    fallbackConfig = HeliumFallbackConfig(
        fallbackView = fallbackView,
        useLoadingState = true, // Show a loading state before fallback
        loadingBudgetInMs = 3000, // Global loading budget (in milliseconds)
        perTriggerLoadingConfig = mapOf(
            "onboarding" to HeliumFallbackConfig(loadingBudgetInMs = 4000),
            "quick_upgrade" to HeliumFallbackConfig(useLoadingState = false)
        )
    )
)