Native Android (Kotlin) Integration Guide
This guide walks you through integrating hober-android-sdk into a native Android (Kotlin) application — from Gradle setup through notification display customisation.
Minimum SDK: API 26 (Android 8.0 Oreo)
For the full API surface, see the API Reference.
Prerequisites
- Android Studio Hedgehog (2023.1.1) or later
- An active Hober account with a valid SDK key and channel ID
- A Firebase project with Cloud Messaging enabled and
google-services.jsondownloaded - minSdkVersion set to 26 or higher in your
build.gradle
Step 1 — Add the Gradle Dependency
Add mavenCentral() to your project-level build.gradle (or settings.gradle if you use dependencyResolutionManagement):
// Project-level build.gradle
repositories {
mavenCentral()
google()
}
Then add the SDK dependency to your app-level build.gradle:
dependencies {
implementation("io.hober:sdk:1.0.0")
// Firebase Messaging is a required peer dependency
implementation("com.google.firebase:firebase-messaging:24.0.0")
}
Apply the Google Services plugin at the bottom of the same file:
plugins {
id("com.google.gms.google-services")
}
Step 2 — Place google-services.json
Download google-services.json from the Firebase console (Project Settings > Your Apps > Android app) and place it in your app module directory:
MyApp/
├── app/
│ ├── google-services.json ← here
│ ├── src/
│ └── build.gradle
└── build.gradle
Step 3 — Initialise in Application.onCreate()
Create an Application subclass (if you don't have one) and call Hober.init() as early as possible:
import android.app.Application
import io.hober.sdk.Hober
import io.hober.sdk.android.AppContextImpl
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Hober.init(
context = AppContextImpl(this),
sdkKey = "YOUR_SDK_KEY",
channelId = "default"
)
}
}
Register MyApplication in AndroidManifest.xml:
<application
android:name=".MyApplication"
... >
Your sdkKey and channelId are available in the Hober dashboard under Settings > SDK Keys.
Step 4 — Request the POST_NOTIFICATIONS Permission (Android 13+)
Android 13 (API 33) introduced the POST_NOTIFICATIONS runtime permission. You must request it before notifications can be displayed to the user.
Declare the permission in AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Request it at a meaningful moment — after the user has had a chance to understand why notifications are useful. Avoid requesting it immediately on first launch:
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
class OnboardingActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
// Permission granted — Hober can display notifications
} else {
// User denied or dismissed — guide them to Settings
showPermissionRationale()
}
}
fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED
) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
// Below Android 13 / API 33, the permission is granted automatically
}
private fun showPermissionRationale() {
// Direct user to Settings > Apps > YourApp > Notifications
}
}
Build.VERSION_CODES.TIRAMISU equals API 33 (Android 13). Devices on API 26–32 do not require the runtime permission; notifications are enabled by default for installed apps.
Step 5 — Set Up HoberFirebaseMessagingService
Automatic token registration (recommended)
Extend HoberFirebaseMessagingService to handle FCM token updates and incoming messages automatically:
import io.hober.sdk.fcm.HoberFirebaseMessagingService
class MyFirebaseService : HoberFirebaseMessagingService()
Register it in AndroidManifest.xml inside the <application> block:
<service
android:name=".MyFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
HoberFirebaseMessagingService overrides onNewToken and onMessageReceived so that tokens are forwarded to the Hober backend automatically whenever FCM rotates them.
Manual token registration
If you need to extend your own FirebaseMessagingService subclass or you obtain the FCM token through a third-party library, call Hober.registerDevice(token) directly:
import com.google.firebase.messaging.FirebaseMessaging
import io.hober.sdk.Hober
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
Hober.registerDevice(token = token)
}
Also override onNewToken in your service to keep the registration current:
override fun onNewToken(token: String) {
super.onNewToken(token)
Hober.registerDevice(token = token)
}
Use HoberFirebaseMessagingService (automatic) unless you already have an existing FirebaseMessagingService that you cannot replace.
Step 6 — Identify a Subscriber
After the user logs in or completes sign-in, associate them with a Hober subscriber record:
import io.hober.sdk.Hober
// Call this after the user has been authenticated
fun onUserLoggedIn(user: User) {
Hober.identifySubscriber(
externalId = user.id,
email = user.email,
attributes = mapOf("plan" to user.plan, "locale" to user.locale)
) { result ->
when (result) {
is SubscriberResult.Success -> { /* proceed */ }
is SubscriberResult.Error -> { /* log and retry */ }
}
}
}
| Parameter | Type | Required | Description |
|---|---|---|---|
externalId | String | Yes | Your internal user ID |
email | String? | No | User email for segmentation |
attributes | Map<String, String>? | No | Custom key-value attributes |
Call Hober.identifySubscriber again whenever the subscriber's data changes (for example, after a plan upgrade).
Step 7 — Customise Notification Display
Notification icon
Place a white, alpha-only PNG in res/drawable/ and reference it in your Hober.init() call or via the Hober dashboard meta-data:
<!-- AndroidManifest.xml — inside <application> -->
<meta-data
android:name="io.hober.sdk.default_notification_icon"
android:resource="@drawable/ic_notification" />
Notification accent color
<meta-data
android:name="io.hober.sdk.default_notification_color"
android:resource="@color/hober_notification_accent" />
Define the color in res/values/colors.xml:
<color name="hober_notification_accent">#4F46E5</color>
Notification channel importance
Hober creates a default notification channel on first launch. Override its importance using the channelId you passed to Hober.init():
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
import androidx.core.content.getSystemService
fun createHoberNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"default", // must match channelId in Hober.init()
"Push Notifications",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Hober promotional and transactional notifications"
}
context.getSystemService<NotificationManager>()
?.createNotificationChannel(channel)
}
}
Call createHoberNotificationChannel() before Hober.init() so the channel exists when the SDK first fires a notification. Use IMPORTANCE_DEFAULT for standard alerts or IMPORTANCE_HIGH for heads-up banners.
Complete Application Boilerplate
Copy-paste this as a starting point and fill in your credentials:
import android.app.Application
import io.hober.sdk.Hober
import io.hober.sdk.android.AppContextImpl
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 1. Create the notification channel before init (Android 8.0+)
createHoberNotificationChannel(this)
// 2. Initialise the Hober SDK
Hober.init(
context = AppContextImpl(this),
sdkKey = "YOUR_SDK_KEY",
channelId = "default"
)
}
private fun createHoberNotificationChannel(context: android.content.Context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = android.app.NotificationChannel(
"default",
"Push Notifications",
android.app.NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Hober promotional and transactional notifications"
}
val manager = context.getSystemService(android.app.NotificationManager::class.java)
manager.createNotificationChannel(channel)
}
}
}
Companion AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for Android 13+ -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Required for FCM (added automatically by google-services plugin on API < 33) -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp">
<!-- Hober notification icon and color -->
<meta-data
android:name="io.hober.sdk.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="io.hober.sdk.default_notification_color"
android:resource="@color/hober_notification_accent" />
<!-- Firebase Messaging Service -->
<service
android:name=".MyFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
Troubleshooting
FCM not delivering notifications
- Verify
google-services.jsonis current. Download a fresh copy from the Firebase console if you recently added the Android app or changed package names. - Check FCM registration token. Add a log in
onNewTokento confirm the token is generated and forwarded to Hober. - Confirm the Hober SDK key and channel ID match the values in the Hober dashboard.
- Test with the Firebase console using Cloud Messaging > Send test message before involving Hober, to rule out an FCM configuration issue.
- Check network/battery restrictions. Doze mode and manufacturer battery optimisations can prevent FCM delivery in the background. Whitelist your app in device battery settings for testing.
Notification channel not found
Android 8.0 (API 26) requires all notifications to be posted to a NotificationChannel. If Hober logs a warning about the channel or notifications are silently dropped:
- Ensure
createNotificationChannel()is called beforeHober.init(). - The
channelIdpassed toHober.init()must exactly match theNotificationChannelID you created. - Deleted channels cannot be recreated with the same ID in the same session; reinstall the app to reset channel state during development.
POST_NOTIFICATIONS permission denied on Android 13
If notifications are not appearing on Android 13+ devices:
- Confirm
android.permission.POST_NOTIFICATIONSis declared inAndroidManifest.xml. - Check current permission status with
ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS). - Android only shows the system permission dialog once. If denied, the user must manually re-enable notifications in Settings > Apps > YourApp > Notifications.
- Use
ActivityCompat.shouldShowRequestPermissionRationale()to determine whether to show an in-app explanation before re-requesting.
API Reference
For a complete list of classes, methods, and parameters, see the generated Dokka/KDoc API Reference. The API reference is published alongside the Maven Central artifact and covers Hober, HoberFirebaseMessagingService, AppContextImpl, SubscriberResult, and all supporting types.