Deep linking, attribution, and smart links for Android.
Part of the Grovs open-source mobile linking platform.
Quick Start · API Reference · Full Docs
The Grovs Android SDK provides deep linking, app links, link generation, in-app messaging, revenue tracking, and attribution for your Android apps. It supports both Kotlin and Java.
- Deep linking & app links — route users to the right in-app screen, even after install
- Smart link generation — create trackable links with metadata, custom redirects, and UTM parameters
- In-app messaging — display messages and announcements from the Grovs dashboard
- Push notifications — receive push notifications for dashboard-sent messages via Firebase Cloud Messaging
- Revenue tracking — log Google Play Billing and custom purchases with automatic attribution
- User identity — attach user IDs and attributes for analytics and segmentation
- Self-hosting support — point the SDK at your own backend
- Android API 21+ (Android 5.0)
- Kotlin 1.6+ or Java 8+
- Android Studio Arctic Fox+
Add the Grovs dependency to your app-level build.gradle:
dependencies {
implementation("io.grovs:Grovs:1.1.1")
}Configure the SDK in your Application class:
import io.grovs.Grovs
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Grovs.configure(this, "your-api-key", useTestEnvironment = false)
// Optional: enable debug logging
Grovs.setDebug(LogLevel.INFO)
// Optional: set user identity for analytics
Grovs.identifier = "user_id_from_your_app"
Grovs.attributes = mapOf("name" to "John Doe", "plan" to "premium")
}
}For self-hosted backends, pass the baseURL parameter (domain only — the SDK appends the API path):
Grovs.configure(this, "your-api-key", useTestEnvironment = false, baseURL = "https://your-domain.com")In your launcher activity, forward lifecycle events to the SDK:
class MainActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
Grovs.onStart(this)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Grovs.onNewIntent(intent, this)
}
}Add these intent filters to your launcher activity in AndroidManifest.xml:
<!-- Custom URL scheme -->
<intent-filter>
<data android:scheme="your_app_scheme" android:host="open" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- App links (production) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="your_app_host" />
</intent-filter>
<!-- App links (test) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="your_app_test_host" />
</intent-filter>Register a listener in your launcher activity to receive deep link events:
Grovs.setOnDeeplinkReceivedListener(this) { deeplinkDetails ->
// Route the user based on payload data
val link = deeplinkDetails.link
val payload = deeplinkDetails.data
val tracking = deeplinkDetails.tracking
payload?.get("screen")?.let { screen ->
navigateTo(screen as String)
}
}Or use Kotlin Flow for a coroutine-based approach:
lifecycleScope.launch {
Grovs.Companion::openedLinkDetails.flow.collect { deeplinkDetails ->
deeplinkDetails?.let {
Log.d("Grovs", "Link: ${it.link}, data: ${it.data}")
}
}
}You can also retrieve details for a specific link path:
// Using a callback
Grovs.linkDetails(path = "/my-link-path", lifecycleOwner = this) { details, error ->
details?.let { Log.d("Grovs", "Details: $it") }
}
// Using coroutines
val details = Grovs.linkDetails(path = "/my-link-path")Create smart links with metadata, payload data, and tracking parameters:
Grovs.generateLink(
title = "Check out this product",
subtitle = "Limited time offer",
imageURL = "https://example.com/image.jpg",
data = mapOf("productId" to "12345", "screen" to "product_detail"),
tags = listOf("promotion", "share"),
tracking = TrackingParams(
utmCampaign = "spring_sale",
utmSource = "in_app",
utmMedium = "share_button"
),
lifecycleOwner = this,
listener = { link, error ->
link?.let { Log.d("Grovs", "Generated: $it") }
error?.let { Log.e("Grovs", "Error: $it") }
}
)Or using coroutines:
lifecycleScope.launch {
try {
val link = Grovs.generateLink(
title = "Check out this product",
subtitle = "Limited time offer",
imageURL = "https://example.com/image.jpg",
data = mapOf("productId" to "12345"),
tags = listOf("promotion"),
tracking = TrackingParams(
utmCampaign = "spring_sale",
utmSource = "in_app",
utmMedium = "share_button"
)
)
Log.d("Grovs", "Generated: $link")
} catch (e: GrovsException) {
Log.e("Grovs", "Error: ${e.message}")
}
}Override where a link sends users on each platform:
val redirects = CustomRedirects(
ios = CustomLinkRedirect(link = "https://example.com/ios-promo"),
android = CustomLinkRedirect(link = "https://example.com/android-promo"),
desktop = CustomLinkRedirect(link = "https://example.com/desktop-promo", openAppIfInstalled = false)
)
Grovs.generateLink(
title = "Special offer",
data = mapOf("promoId" to "summer25"),
customRedirects = redirects,
lifecycleOwner = this,
listener = { link, error ->
link?.let { Log.d("Grovs", "Generated: $it") }
}
)Launch a share intent after generating a link:
Grovs.generateLink(
title = "Share this",
data = mapOf("itemId" to "abc"),
lifecycleOwner = this,
listener = { link, _ ->
link?.let {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, it)
type = "text/plain"
}
startActivity(Intent.createChooser(sendIntent, "Share via"))
}
}
)If console messages have automatic display enabled in your dashboard, they will appear in your app without any additional integration.
To receive push notifications for messages sent from the Grovs dashboard:
1. Add Firebase Cloud Messaging — If your app doesn't already use Firebase, add your app in the Firebase Console, download google-services.json, and add the dependencies:
// project-level build.gradle
plugins {
id("com.google.gms.google-services") version "4.4.2" apply false
}
// app-level build.gradle
plugins {
id("com.google.gms.google-services")
}
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
implementation("com.google.firebase:firebase-messaging")
}2. Upload your Firebase credentials — In the Firebase Console, go to Project Settings → Service Accounts and generate a new private key. Upload the JSON key file and enter your Firebase Project ID in the Grovs dashboard under Android Setup → Push Notifications.
3. Request notification permission (Android 13+):
import android.Manifest
import android.os.Build
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />4. Pass the FCM token to Grovs:
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
Grovs.pushToken = token
}Also update the token when it refreshes:
class MyMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
Grovs.pushToken = token
}
}Register the service in AndroidManifest.xml:
<service
android:name=".MyMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>Push notifications require a physical device or an emulator with Google Play Services.
// Show the messages list as a modal fragment
Grovs.displayMessagesFragment {
// Fragment was dismissed
}
// Get unread count for badges
lifecycleScope.launch {
val count = Grovs.numberOfUnreadMessages()
Log.d("Grovs", "Unread: $count")
}Revenue tracking is currently in beta.
- Enable revenue tracking in the Grovs dashboard under Settings → Revenue Tracking
- Configure Google Play Real-Time Developer Notifications — the Grovs dashboard provides an automated setup script under Developers → Android Setup → Revenue, or you can configure Pub/Sub manually
// In your PurchasesUpdatedListener
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
Grovs.logInAppPurchase(purchase.originalJson)
}
}
}The SDK automatically extracts price, currency, and product info from the purchase JSON. Duplicates are filtered.
Grovs.logCustomPurchase(
type = PaymentEventType.BUY,
priceInCents = 999, // $9.99
currency = "USD",
productId = "premium_monthly"
)Use CANCELLATION and REFUND payment event types for cancellations and refunds. For Google Play purchases, these are detected automatically via Real-Time Developer Notifications.
| Property | Type | Description |
|---|---|---|
useTestEnvironment |
Boolean |
Enable or disable test environment |
identifier |
String? |
User ID shown in dashboard and reports |
attributes |
Map<String, Any>? |
User attributes for analytics |
openedLinkDetails |
DeeplinkDetails? |
Kotlin Flow emitting deep link details |
pushToken |
String? |
FCM device token for push notifications |
| Method | Description |
|---|---|
configure(application, apiKey, useTestEnvironment, baseURL) |
Initialize the SDK |
setSDK(enabled) |
Enable or disable the SDK |
setDebug(level) |
Set logging level (INFO, ERROR) |
onStart(activity) |
Forward launcher activity's onStart() |
onNewIntent(intent, activity) |
Forward launcher activity's onNewIntent() |
generateLink(...) |
Generate a smart link (callback or coroutine) |
setOnDeeplinkReceivedListener(activity, listener) |
Register deep link listener |
linkDetails(path, ...) |
Get details for a link path (callback or coroutine) |
displayMessagesFragment(onDismissed) |
Show messages modal fragment |
numberOfUnreadMessages() |
Get unread message count (suspend) |
logInAppPurchase(originalJson) |
Log a Google Play Billing purchase |
logCustomPurchase(type, priceInCents, currency, productId, startDate) |
Log a custom purchase |
Full API reference: docs.grovs.io/docs/sdk/android/api-reference
A demo project is available at grovs-io/grovs-android-example-app.
- Adding a Gradle Dependency — add the SDK to your project
- Getting the Package Name — find your application ID
- Getting the SHA-256 Fingerprint — get your signing certificate fingerprint
- Adding an Intent Filter — set up deep link intent filters
Full documentation at docs.grovs.io.
For technical support and inquiries, contact support@grovs.io.
See LICENSE for details.