Docs
grovs.io Start for free

Events & screen tracking

Track custom events and screen views in your Android app with the Grovs SDK.

Requires Android SDK 3.0 or later. Screen views and custom events can also trigger in-app messages (3.1).

Tracking custom events

track records an event with optional properties and tags:

Kotlin
Grovs.track("checkout_completed", properties = mapOf("sku" to "abc", "total" to 42.0), tags = listOf("shop"))
Grovs.track("level_complete", properties = mapOf("level" to 5, "score" to 1200))
Grovs.track("button_tap")

How events are sent:

  • Written to disk and sent in one batch every 30 seconds. A failed batch stays on disk and is retried on the next tick.
  • Up to 1,000 events are kept on the device. Events older than seven days are dropped.
  • Before authentication, events are stored and sent once it completes.
  • While install attribution resolves, events are held so they credit the right link. See Install attribution timing.

Event name rules

Names can't be blank or a reserved system name:

view, open, install, reinstall, app_open, time_spent, reactivation, user_referred, custom, screen_view

Push, in-app and journey event names are refused too: in_app_viewed, in_app_clicked, in_app_dismissed, in_app_answered, in_app_commented, in_app_held, push_open, push_delivered, push_held, journey_entered, journey_held, journey_path. in_app_held, the push_* and the journey_* names are refused since 3.1.

Names are compared after trimming, ignoring case. Rejected events are logged and dropped. Use trackScreenView for screen views, not track("screen_view").

Property values

Supported: strings, booleans, standard numbers, string-keyed maps, lists and arrays. The SDK sanitizes each value, for track and trackScreenView alike:

  • Date, URL and UUID become strings, including when nested.
  • Whole numbers are sent as whole numbers (4, not 4.0).
  • A value JSON can't represent is dropped on its own: a list element from its list, a map entry from its map, a top-level property from the event. Everything else is kept. Dropped top-level properties are logged.
  • That covers NaN, infinity, other types (enums, sets, arbitrary objects), maps with any non-string key (the whole map is dropped), references back to an enclosing collection, and nesting deeper than 16 levels.
  • BigInteger and BigDecimal beyond the Double range are dropped like infinity.
  • Over 8 KB of UTF-8, or more than 8,192 values traversed, omits the whole properties map. The event is still recorded.

Tags

Up to 20 tags per event, each up to 255 characters. Global tags attach to every later event:

Kotlin
Grovs.setGlobalTags(listOf("android", "production"))
 
Grovs.setGlobalTags(null)  // clear

Global and per-event tags are merged, up to 20 combined.

Tracking screen views

Kotlin
Grovs.trackScreenView("Checkout", properties = mapOf("step" to 2))

The same screen reported twice within one second is dropped, so a brief pause/resume or a re-attached Fragment doesn't flood the dashboard. A genuine later return to the screen still counts.

Automatic screen tracking

autoTrackScreenViews defaults to true: the SDK reports Activities and Fragments as screens. When an Activity hosts Fragments, only the Fragment is reported, as iOS does for container view controllers.

  • Modals (DialogFragment, BottomSheetDialogFragment) are overlays, never reported under their own name. Opening one re-resolves the visible screen, so the screen underneath may be reported again unless it was reported in the last second. To report the modal, call trackScreenView when it opens.
  • Tabs using the Navigation component, ViewPager2, setMaxLifecycle or replace() are tracked, because they resume the destination Fragment. Legacy hide()/show() doesn't change Fragment lifecycle, so it isn't tracked. Use trackNavigation, setMaxLifecycle or trackScreenView in your tab handler.
  • Jetpack Compose destinations aren't tracked automatically; the SDK has no Compose dependency. Use trackNavigation or trackScreenView.

To turn it off, pass autoTrackScreenViews = false:

Kotlin
Grovs.configure(this, "your-api-key", useTestEnvironment = false, baseURL = null, autoTrackScreenViews = false)

Grovs.setSDK(false) stops automatic screen tracking along with all other collection.

Jetpack Navigation

With Jetpack Navigation (Navigation Compose or route-based graphs), pass your NavController to track every destination change. That includes bottom-navigation tabs, navigation rails and Compose destinations the lifecycle tracker can't see:

Kotlin
Grovs.trackNavigation(navController)

The name comes from the destination's route, then android:label, then its display name. Call it once per NavController, for example after setting the graph. Repeat calls are ignored. The SDK holds a weak reference, so it doesn't leak the host Activity or Fragment.

With Fragment-based navigation, trackNavigation overlaps the automatic tracker and can report duplicates under different names. Either set autoTrackScreenViews = false and rely on trackNavigation, or use trackNavigation only for Compose and route-based graphs.

Screen name aliases

Map Activity and Fragment class names to friendlier dashboard names:

Kotlin
Grovs.setScreenAliases(mapOf("MainActivity" to "Home", "CartFragment" to "Shopping Cart"))

Aliases sync after authentication. While the SDK is disabled, the latest map is held until it's enabled.

Sessions

Custom events and screen views carry an analytics session. Brief trips to the background keep the session; returning after more than 30 minutes starts a new one. A campaign resolved for a session stays with that session's events and isn't inherited by the next. Queued events keep their original session and attribution.

Next steps

Last updated 2026-10-27
Was this page helpful?