Clix Android SDK is a powerful tool for managing push notifications and user events in your Android application. It provides a simple and intuitive interface for user engagement and analytics.
Add the following to your project's settings.gradle.kts or build.gradle.kts:
repositories {
mavenCentral()
}Add the dependency to your app's build.gradle.kts:
dependencies {
implementation("so.clix:clix-android-sdk:1.3.0")
}- Create or select a project in the Firebase Console.
- Register your app with the Firebase project.
- Download the google-services.json file and add it to your app module directory.
- Android API level 26 (Android 8.0) or later
- Firebase Cloud Messaging
Initialize the SDK with a ClixConfig object. The config is required and contains your project settings.
import so.clix.core.Clix
import so.clix.core.ClixConfig
import so.clix.utils.logging.ClixLogLevel
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Clix.initialize(
context = this,
config = ClixConfig(
projectId = "YOUR_PROJECT_ID",
apiKey = "YOUR_API_KEY",
endpoint = "https://api.clix.so", // Optional: default is https://api.clix.so
logLevel = ClixLogLevel.INFO // Optional: default is INFO
)
)
}
}import kotlinx.coroutines.launch
// Set user ID
lifecycleScope.launch {
Clix.setUserId("user123")
}
// Set user properties
lifecycleScope.launch {
Clix.setUserProperty("name", "John Doe")
Clix.setUserProperties(
mapOf(
"age" to 25,
"premium" to true
)
)
}
// Remove user properties
lifecycleScope.launch {
Clix.removeUserProperty("name")
Clix.removeUserProperties(listOf("age", "premium"))
}
// Remove user ID
lifecycleScope.launch {
Clix.removeUserId()
}import kotlinx.coroutines.launch
// Track an event with properties
lifecycleScope.launch {
Clix.trackEvent(
"signup_completed",
mapOf(
"method" to "email",
"discount_applied" to true,
"trial_days" to 14,
"completed_at" to Instant.now(),
)
)
}// Get device ID
val deviceId = Clix.getDeviceId()
// Get push token
val pushToken = Clix.Notification.getToken()Clix.setLogLevel(ClixLogLevel.DEBUG)
// Available log levels:
// - NONE: Disable logging
// - ERROR: Log errors only
// - WARN: Log warnings and errors
// - INFO: Log info, warnings, and errors
// - DEBUG: Log allConfigure notification handling in your Application class after initializing the SDK:
import so.clix.core.Clix
import so.clix.core.ClixConfig
import so.clix.utils.logging.ClixLogLevel
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// STEP 1: Initialize Clix SDK first
Clix.initialize(
context = this,
config = ClixConfig(
projectId = "YOUR_PROJECT_ID",
apiKey = "YOUR_API_KEY"
)
)
// STEP 2: Configure notification handling after initialization
// Note: autoRequestPermission defaults to false
Clix.Notification.configure(
autoRequestPermission = true, // Set to true to automatically request permission
autoHandleLandingURL = true // Set to true to automatically open landing URLs
)
// STEP 3: Optional callbacks (must be called after initialize)
Clix.Notification.onMessage { notificationData ->
// Return true to display the notification, false to suppress it
true
}
Clix.Notification.onNotificationOpened { notificationData ->
// Custom routing (called when user taps notification)
val landingURL = (notificationData["clix"] as? Map<*, *>)?.get("landing_url") as? String
if (landingURL != null) {
// Handle custom routing
}
}
Clix.Notification.onFcmTokenError { error ->
Log.e("MyApp", "FCM token error: ${error.message}", error)
// Handle token registration failures (e.g., Firebase config issues, network errors)
}
}
}Important: All Clix.Notification methods must be called after Clix.initialize(). Calling them before initialization will result in an error.
- The
notificationDatamap is the full FCM payload as delivered to the device; it mirrors iOS’suserInfodictionary. - Every Clix notification callback (
onMessage,onBackgroundMessage,onNotificationOpened) passes this map through untouched, so you can inspect both the serialized"clix"block and any custom keys your backend adds. notificationData["clix"]holds the Clix metadata JSON, while all other keys represent app-specific data.
Or request permission manually:
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
val granted = Clix.Notification.requestPermission()
if (granted) {
// Permission granted
}
}
}
}Inherit from ClixMessagingService:
import com.google.firebase.messaging.RemoteMessage
import so.clix.notification.ClixMessagingService
class MyMessagingService : ClixMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
// Custom notification handling
}
override fun onNewToken(token: String) {
super.onNewToken(token)
// Custom token handling
}
}Features:
- Automatic device token registration and updates
- Push notification event tracking
- Duplicate notification prevention
- Deep linking support (automatic landing URL handling)
- Use
Clix.Notification.configure(autoHandleLandingURL = false)to disable automatic landing URL handling
The SDK automatically handles landing URLs in push notifications. When a user taps a notification:
- Automatic handling (default): The SDK opens the
landing_urlfrom the notification payload - Custom handling: Disable automatic handling and implement your own routing:
// Disable automatic handling
Clix.Notification.configure(
autoRequestPermission = false,
autoHandleLandingURL = false
)
// Handle custom routing
Clix.Notification.onNotificationOpened { notificationData ->
val clixData = notificationData["clix"] as? Map<*, *>
val landingURL = clixData?.get("landing_url") as? String
if (landingURL != null) {
// Parse and route to specific app screen
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(landingURL))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}Payload structure:
{
"clix": {
"message_id": "msg_123",
"landing_url": "myapp://screen/detail?id=123",
"campaign_id": "campaign_456"
}
}configure(autoRequestPermission:autoHandleLandingURL:): Configure push notification handlingonMessage(handler:): Register handler for foreground messagesonBackgroundMessage(handler:): Register handler for background messagesonNotificationOpened(handler:): Register handler for notification tapsonFcmTokenError(handler:): Register handler for FCM token errorsrequestPermission(): Request notification permissionsgetToken(): Get current FCM tokendeleteToken(): Delete FCM tokengetPermissionStatus(): Get current permission statussetPermissionGranted(isGranted:): Update permission status on server
All SDK operations can throw exceptions. Always handle potential errors:
try {
Clix.setUserId("user123")
} catch (e: Exception) {
Log.e("Clix", "Failed to set user ID", e)
}The SDK is thread-safe and all operations can be called from any thread. Coroutine-based operations will automatically wait for SDK initialization to complete.
The autoRequestPermission parameter defaults to false. If you're not using automatic permission requests, you must manually notify Clix when users grant or deny push permissions.
When using autoRequestPermission = false (the default), call Clix.Notification.setPermissionGranted() after requesting push permissions in your app:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), PERMISSION_REQUEST_CODE)
}
// In your permission result callback
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == PERMISSION_REQUEST_CODE) {
val granted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
// ✅ Notify Clix SDK about permission status
lifecycleScope.launch {
Clix.Notification.setPermissionGranted(granted)
}
}
}If you're experiencing FCM token registration failures, use the error handler to diagnose:
Clix.Notification.onFcmTokenError { error ->
Log.e("FCM", "Token error: ${error.message}", error)
// Common causes:
// - Missing or invalid google-services.json
// - Network connectivity issues
// - Firebase service errors
// - Server-side token registration failures
}Common FCM token errors:
- "SERVICE_NOT_AVAILABLE": Network issues or Firebase service down
- "INVALID_SENDER": Incorrect Firebase configuration (check google-services.json)
- Token registration failure: Backend API errors when saving token
If push notifications aren't working, verify:
- ✅
google-services.jsonis added to your app module - ✅ Firebase Cloud Messaging is properly configured
- ✅
ClixMessagingServiceis declared inAndroidManifest.xml - ✅
Clix.Notification.setPermissionGranted()is called after requesting permissions (when not using auto-request) - ✅ Testing on a real device or emulator with Google Play Services
- ✅ Debug logs show "New token received" message
- ✅ Use
onFcmTokenError()handler to catch token registration errors
If you continue to experience issues:
- Enable debug logging (
ClixLogLevel.DEBUG) - Check Logcat for Clix log messages
- Verify your device appears in the Clix console Users page
- Check if
push_tokenfield is populated for your device - Create an issue on GitHub with logs and configuration details
If you are using Proguard, the following rules are applied automatically:
-keep class so.clix.** { *; }
-keep class com.google.firebase.** { *; }
This project is licensed under the MIT License. See the LICENSE file for details.
See the full release history and changes in the CHANGELOG.md file.
We welcome contributions! Please read the CONTRIBUTING.md guide before submitting issues or pull requests.