VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Kotlin SDK
Kotlin quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
Kotlin SDK reference
Errors
Diagnostic codes
Troubleshooting

Track screens

Record screen views automatically for activities, fragments and Compose Navigation.

The SDK records a $screen event each time a person lands on a new screen. Each event carries the previous screen and how long the person stayed there, so one event describes a whole transition. Activity tracking is on by default.

Activities

When an activity resumes, the SDK records it with the class name as the screen name once the resume has completed. An activity that finishes before then is not recorded. Returning from the background does not record the same activity again.

Compose Navigation

Single-activity apps navigate inside one activity, so attach the tracker to your NavController:

import com.voidhash.sdk.VoidhashScreenTracking

val navController = rememberNavController()
DisposableEffect(navController) {
    val tracking = VoidhashScreenTracking.attach(navController)
    onDispose { tracking.close() }
}

The screen name is the destination route pattern, such as item/{id}, and the path is the route with arguments filled in, such as item/42. While a controller is attached, activity resumes are not reported, including the resume of the activity that attaches it. A rotation reports neither the recreated activity nor the current destination again. Closing the handle detaches the listener and lets activity screens through again.

Fragments

Turn on fragment tracking to record fragments as screens:

import com.voidhash.sdk.ScreenTrackingOptions

Voidhash.configure(
    context = this,
    publishableKey = "vh_pk_...",
    options = VoidhashOptions(screenTracking = ScreenTrackingOptions(fragments = true)),
)

Fragment tracking needs androidx.fragment in your app.

Each resumed fragment is recorded as a screen. An activity that shows a tracked fragment is not reported itself; other activities still are. These fragments are skipped:

  • Fragments without a view, which libraries use for background work.
  • Hidden fragments.
  • Dialogs, including bottom sheets. The screen underneath stays the current screen.
  • Framework fragments such as NavHostFragment.

A fragment is named by the first of these that it has:

  1. The route of the Navigation destination it shows, or else the destination's label. This needs Navigation 2.4 or later; with an older release the next two apply.
  2. The tag you gave it, for example with replace(R.id.container, fragment, "settings"). Tags that libraries assign themselves, such as ViewPager2's f0, are ignored.
  3. Its class name.

Release builds that use R8 rename every fragment class that no layout or navigation graph mentions. Fragments created in code, such as ViewPager2 pages or tabs you switch with fragment transactions, then report names like a or c0 that change with every release. Give those fragments a tag, or keep their class names with this rule in your app's proguard-rules.pro:

-keepnames class * extends androidx.fragment.app.Fragment

Custom navigation

Call screen() yourself when your app drives navigation another way:

voidhash.screen("Onboarding step 2", mapOf("step" to 2))

Event properties

PropertyMeaning
$screen_nameStable identity of the screen. Never contains ids.
$screen_pathConcrete location, including route arguments.
$screen_titleThe activity title when one is set.
$previous_screen_nameScreen the person came from. null on the first screen.
$previous_screen_pathPath of the previous screen.
$previous_screen_duration_msMilliseconds spent on the previous screen, including time in the background.
$screen_sourceandroid-activity, android-fragment, compose-navigation or manual.

Options

VoidhashOptions(
    screenTracking = ScreenTrackingOptions(
        automatic = true,
        fragments = false,
        includeParams = false,
        mapScreen = { view -> if (view.name.startsWith("Debug")) null else view },
    ),
)

Set automatic to false to stop the SDK from recording activities as screens. includeParams adds Compose route arguments to each event as $screen_params, at most 20 keys. It is off by default because arguments often carry ids. mapScreen lets you rename a screen or drop it by returning null. If it throws, the SDK drops that screen and reports a SCREEN_MAPPER_FAILED diagnostic instead of letting the exception reach your activity or navigation callbacks.

To stop collecting screen views for a project without an app release, use Settings → Events in Studio. See Events.

Next steps

  • Capture analytics.
  • Reports built from screen views.
  • Paywalls, whose screens are recorded too.

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousGroupsNextDevelopment mode

On this page

ActivitiesCompose NavigationFragmentsCustom navigationEvent propertiesOptionsNext steps