Track screenviews
The SDK supports two screenview autocapture modes:
- Enhanced screenview autocapture (default mode)
- Activity-only autocapture
Enhanced screenview autocapture
Section titled Enhanced screenview autocaptureEnhanced screenview autocapture reports each Fragment, Jetpack Compose destination, or custom screen as its own screenview, and attributes each interaction to the screen where it occurred. Some screens are captured with no code change, and others need an annotation or a wrapper depending on how your app navigates.
Enhanced screenview autocapture is the default mode. Autocapture itself is off until you set enableViewAutocapture = true:
CSQ.start( context = this, StartConfig.withEnvironmentId( id = "YOUR_ENVIRONMENT_ID", options = AnalyticsOptions( enableViewAutocapture = true // disablePageviewAutocapture = false by default, so screenviews are captured ) ))Screenview options
Section titled Screenview optionsTwo AnalyticsOptions settings control automatic screenviews:
| Option | Default | Effect |
|---|---|---|
enableViewAutocapture | false | Registers interaction autocapture for Views and Jetpack Compose. Set it to true to collect interactions. |
disablePageviewAutocapture | false | Set it to true to turn off automatic screenviews. Interaction autocapture stays on. In enhanced mode, the default, interactions attach to screenviews tracked manually with CSQ.trackScreenview(). |
Set up screens for your navigation
Section titled Set up screens for your navigationPick the setup that matches each part of your app. A mixed app can use more than one approach.
| Your app uses | Setup |
|---|---|
| An Activity for each screen | Annotate each screen Activity with @CsqScreenView. |
| Fragments inside a screen container | Implement CsqScreenViewContainerOwner on the host Activity. |
| Navigation Compose (Nav2) destinations | Automatic. Name typed routes with @CsqScreenName, or configure string routes at startup. |
| Navigation 3 (Nav3) entries | Automatic. Optionally name a key class with @CsqScreenName. |
| Custom Compose navigation | Wrap each screen's content with CsqScreenView. |
If your app uses a single Activity with Nav2 or Nav3, leave the host Activity unannotated so it doesn't create an extra Activity screenview.
Activities
Section titled ActivitiesWhen an Activity itself represents a screen, annotate it:
import androidx.appcompat.app.AppCompatActivityimport com.contentsquare.autocapture.pageview.CsqScreenView
@CsqScreenView(screenName = "Checkout")class CheckoutActivity : AppCompatActivity()If you omit screenName, the screenview uses the Activity class name.
An unannotated Activity with no eligible child screen doesn't produce an automatic screenview. Annotate it to collect it.
Fragments
Section titled FragmentsWhen Fragments represent screens, identify the container that directly hosts them:
import androidx.fragment.app.FragmentActivityimport com.contentsquare.autocapture.pageview.CsqScreenViewContainerOwner
class CheckoutFlowActivity : FragmentActivity(), CsqScreenViewContainerOwner {
override val csqScreenViewContainerId: Int get() = R.id.fragment_container}Visible Fragments in that container are captured automatically, and the host Activity needs no annotation. To report a readable name instead of the class name, annotate the Fragment:
import androidx.fragment.app.Fragmentimport com.contentsquare.autocapture.pageview.CsqScreenName
@CsqScreenName(name = "Payment")class PaymentFragment : Fragment()The naming annotation doesn't enable capture for Fragments outside the configured container.
Navigation Compose (Nav2)
Section titled Navigation Compose (Nav2)Supported Nav2 destinations are captured automatically. For a typed destination, add an optional name to its route class:
import com.contentsquare.autocapture.pageview.CsqScreenNameimport kotlinx.serialization.Serializable
@Serializable@CsqScreenName(name = "Product Details")data class Product(val id: String)The screenview name is the annotation value, "Product Details". Route arguments such as the product ID don't create separate screens, and your navigation graph stays unchanged.
String routes such as composable("product/{id}") are also captured automatically. By default, they use the stable route pattern, not the argument value. To name or exclude them, configure their declared patterns in Application.onCreate, before any NavHost is composed:
import com.contentsquare.autocapture.AutocaptureSDK
AutocaptureSDK.configureNav2StringRoutes( screenNames = mapOf("product/{id}" to "Product Details"), ignoredRoutes = setOf("checkout"))Keep these rules in mind:
- Use exact declared patterns, such as
product/{id}, rather than paths with argument values. - Matching is case-sensitive, surrounding whitespace is trimmed, and wildcards aren't supported.
- The configuration applies across your app, so identical patterns in different graphs share the same settings.
- Supply all settings in one startup call. Each call replaces the entire configuration, and calling with no arguments clears it.
- If a route appears in both collections, it's excluded.
- Updating the configuration after navigation starts isn't supported.
Navigation 3 (Nav3)
Section titled Navigation 3 (Nav3)Supported Nav3 entries are captured automatically. To name one, annotate its key class:
import androidx.navigation3.runtime.NavKeyimport com.contentsquare.autocapture.pageview.CsqScreenNameimport kotlinx.serialization.Serializable
@Serializable@CsqScreenName(name = "Product Details")data class ProductDetailsKey(val productId: String) : NavKeyWithout the annotation, the screen name comes from the key class. Argument values such as productId don't create separate screens.
Custom Compose navigation
Section titled Custom Compose navigationWhen a screen isn't represented by a supported navigation API, wrap its content:
import androidx.compose.runtime.Composableimport com.contentsquare.autocapture.compose.CsqScreenView
@Composablefun PaymentContent() { CsqScreenView(screenName = "Payment") { PaymentScreen() }}The wrapper defines a screen while it's active. Use a stable name without user identifiers, order numbers, or other values that change. Don't add the wrapper to a destination that is already captured automatically unless it should introduce a separate screen boundary.
When a screen closes, remove the wrapper from the composition. An empty wrapper that stays in the composition keeps its screen active.
Exclude a screen
Section titled Exclude a screenTo stop a typed Nav2 route or Nav3 key from generating automatic screenviews, annotate its class with @CsqIgnoreScreenView:
import com.contentsquare.autocapture.pageview.CsqIgnoreScreenViewimport kotlinx.serialization.Serializable
@Serializable@CsqIgnoreScreenViewobject CheckoutFor string routes, use ignoredRoutes as shown in Navigation Compose (Nav2). An excluded screen keeps its interaction capture, but the following interactions stay attributed to the last screenview that was collected.
Verify your integration
Section titled Verify your integrationOpen Live Data Feed, then navigate through your app, for example from Product to Checkout and Payment. Check the collected screenviews and confirm that interactions are attributed to the screen where they occurred.
| Action | Expected result |
|---|---|
| Open an eligible screen | A screenview appears with its configured name, or its class or route name. |
| Tap a button on a screen | The interaction references that screen. |
| Open an excluded screen | No screenview appears. Later interactions can stay attributed to the previous screen. |
| Trigger ordinary recomposition | No duplicate screenview for the unchanged screen. |
| Rotate the device on the same screen | No duplicate screenview solely for the configuration change. |
| Background and reopen the app | Eligible screens can generate a new screenview. |
Each intended transition should generate one screenview. If your app also calls CSQ.trackScreenview() from an earlier integration, check for duplicates.
Troubleshooting
Section titled Troubleshooting| Symptom | Check |
|---|---|
| No automatic screenviews | Confirm that the SDK is started, that capture is active, and that enableViewAutocapture is true and disablePageviewAutocapture is false. |
| An Activity or Fragment is missing | Confirm the Activity annotation, or the Fragment's host container. |
| A route's name or exclusion has no effect | Confirm that it's a supported typed Nav2 route or Nav3 key, and that you annotated its class. |
| A string route's name or exclusion has no effect | Call configureNav2StringRoutes before the first NavHost is composed, use the exact declared pattern, and check that no later call replaced it. |
| Autocapture imports don't resolve | Confirm that your SDK version includes enhanced screenview autocapture. |
| Extra screenviews appear | Check for an annotated host Activity in a single-Activity app, unnecessary wrappers, or duplicate manual calls. |
| A closed screen stays active | Remove the whole CsqScreenView wrapper from the composition, and check for another visible wrapper with the same name. |
Activity-only autocapture
Section titled Activity-only autocaptureActivity-only autocapture generates a screenview each time an Activity enters the foreground, with no per-screen setup. With enhanced screenview autocapture, an unannotated Activity entering the foreground no longer generates an automatic screenview just because it became visible.
To keep Activity-only screenviews, call AutocaptureSDK.useLegacyActivityPageviewAutocapture() before CSQ.start(). Autocapture must still be enabled with enableViewAutocapture = true, and disablePageviewAutocapture must be false.
import android.app.Applicationimport com.contentsquare.CSQimport com.contentsquare.StartConfigimport com.contentsquare.api.model.AnalyticsOptionsimport com.contentsquare.autocapture.AutocaptureSDK
class MyApplication : Application() { override fun onCreate() { super.onCreate()
AutocaptureSDK.useLegacyActivityPageviewAutocapture() CSQ.start( context = this, StartConfig.withEnvironmentId( id = "YOUR_ENVIRONMENT_ID", options = AnalyticsOptions( enableViewAutocapture = true, disablePageviewAutocapture = false ) ) ) }}
