A Kotlin library for integrating TenMax Beacon tracking functionality into Android applications. This public distribution provides pre-compiled AAR files and complete integration examples for quick and easy SDK integration.
- Beacon Detection: Supports Bluetooth Low Energy (BLE) beacon detection with continuous scanning
- Background Scanning: Continues beacon scanning even when the app is in the background using Foreground Service
- Frequency Capping: Intelligent frequency control to prevent spam notifications with configurable intervals
- Beacon Deduplication: Prevents processing duplicate beacon data within 30-second time windows (configurable)
- Network Connectivity: Automatic network connectivity checking before API calls
- Notification Management: Handles local notifications with click tracking and creative data content support
- Environment Support: Separate configurations for staging and production environments
- Thread Safety: All operations are thread-safe with proper concurrent queue management and automatic main thread callback execution
- Permission Management: Comprehensive permission handling for Bluetooth, Location, and Notification permissions
- Auto-restart Support: Automatic SDK restart after device boot
For a quick integration, follow these essential steps:
- Add the SDK to your project using AAR file or demo project
- Add required permissions to AndroidManifest.xml (see Permission Requirements)
- Initialize the SDK in your Application or MainActivity
- Implement the callback interface to handle events
- Start scanning when appropriate
-
Copy the AAR file to your project
cp libs/beacon-sdk-release.aar your-project/app/libs/
-
Configure build.gradle
dependencies { implementation files('libs/beacon-sdk-release.aar') }
-
Copy the demo project
cp -r sdkdemo your-new-project cd your-new-project
-
Build and run
./gradlew :sdkdemo:assembleDebug ./gradlew :sdkdemo:installDebug
import com.tenmax.beacon.TenMaxAdBeaconSDK
import com.tenmax.beacon.TenMaxAdBeaconCallback
import com.tenmax.beacon.model.ClientProfile
import com.tenmax.beacon.model.TenMaxAdCreative
import com.tenmax.beacon.model.TenMaxAdBeaconError
import com.tenmax.beacon.api.Environment
// Create client profile
val clientProfile = ClientProfile(
phoneNumber = "0912345678", // Optional: will be persisted and restored
email = "[email protected]", // Optional: will be persisted and restored
appName = "YourAppName",
advertisingId = null // Optional: auto-retrieved from Google Play Services
)
// Implement callback interface
class BeaconCallback : TenMaxAdBeaconCallback {
override fun onInitialized() {
Log.d("BeaconSDK", "SDK initialized successfully")
// Start SDK
TenMaxAdBeaconSDK.getInstance(applicationContext).start()
}
override fun onCreativeReceived(creative: TenMaxAdCreative) {
Log.d("BeaconSDK", "Received creative with data: ${creative.data}")
}
override fun onError(error: TenMaxAdBeaconError) {
Log.e("BeaconSDK", "Error: ${error.message}")
}
override fun onNotificationClicked(creative: TenMaxAdCreative) {
Log.d("BeaconSDK", "Notification clicked with data: ${creative.data}")
// Handle data navigation
}
}
// Initialize SDK
val sdk = TenMaxAdBeaconSDK.getInstance(applicationContext)
sdk.initiate(
clientProfile = clientProfile,
callback = BeaconCallback(),
environment = Environment.PRODUCTION
)
The SDK follows a specific lifecycle pattern:
- getInstance(): Get SDK singleton instance
- initiate(): Initialize SDK with client profile, callback, and environment
- onInitialized(): Callback triggered when initialization completes
- start(): Begin beacon scanning (typically called in onInitialized callback)
- isScanning: Property to check current scanning status (returns true when actively scanning)
- isInitialized: Property to check if SDK has been properly initialized
- stop(): Stop beacon scanning when needed
The SDK prevents duplicate initialization attempts. Use the isInitialized
property to check initialization status:
val sdk = TenMaxAdBeaconSDK.getInstance(applicationContext)
if (!sdk.isInitialized) {
// Safe to initialize
sdk.initiate(clientProfile, callback, Environment.PRODUCTION)
} else {
// Already initialized, can start scanning
sdk.start()
}
Note: You must specify the environment (Environment.STAGE
or Environment.PRODUCTION
) during initialization. Environment can only be set during initialization and cannot be changed later.
import com.tenmax.beacon.model.NotificationConfiguration
val notificationConfig = NotificationConfiguration(
smallIcon = R.drawable.my_notification_icon,
channelName = "My Beacon Notifications",
channelDescription = "Notifications from my beacon app",
foregroundServiceTitle = "My Beacon Scanning",
foregroundServiceChannelName = "My Beacon Service",
foregroundServiceChannelDescription = "Continuously scan for beacons",
foregroundServiceContentText = "Scanning for nearby beacons..."
)
// Initialize SDK with custom notification configuration
sdk.initiate(
clientProfile = clientProfile,
callback = callback,
environment = Environment.PRODUCTION,
notificationConfig = notificationConfig
)
// Update client profile
val updatedProfile = ClientProfile(
phoneNumber = "0987654321",
email = "[email protected]",
appName = "YourAppName",
advertisingId = "updated-advertising-id"
)
TenMaxAdBeaconSDK.getInstance(applicationContext).updateClientProfile(updatedProfile)
// Check if SDK is currently scanning
val isScanning = TenMaxAdBeaconSDK.getInstance(applicationContext).isScanning
Log.d("BeaconSDK", "SDK is scanning: $isScanning")
// Stop SDK
TenMaxAdBeaconSDK.getInstance(applicationContext).stop()
The SDK requires several permissions to function properly:
- Bluetooth permissions: For BLE beacon detection
- Location permissions: Required for BLE scanning on Android
- Foreground Service permissions: For background scanning
- Notification permissions: For displaying notifications (Android 13+)
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class PermissionManager(private val activity: Activity) {
companion object {
private const val PERMISSION_REQUEST_CODE = 100
}
fun requestAllPermissions() {
val permissionsToRequest = mutableListOf<String>()
// Check Bluetooth permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+ requires new Bluetooth permissions
if (!hasPermission(Manifest.permission.BLUETOOTH_SCAN)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH_SCAN)
}
if (!hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH_CONNECT)
}
} else {
// Older versions check traditional Bluetooth permissions
if (!hasPermission(Manifest.permission.BLUETOOTH)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH)
}
if (!hasPermission(Manifest.permission.BLUETOOTH_ADMIN)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH_ADMIN)
}
}
// Check location permissions
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
permissionsToRequest.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
// Check notification permission (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (!hasPermission(Manifest.permission.POST_NOTIFICATIONS)) {
permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS)
}
}
if (permissionsToRequest.isNotEmpty()) {
ActivityCompat.requestPermissions(
activity,
permissionsToRequest.toTypedArray(),
PERMISSION_REQUEST_CODE
)
}
}
private fun hasPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED
}
}
The SDK provides comprehensive error handling through the TenMaxAdBeaconError
class:
class BeaconSDKCallback : TenMaxAdBeaconCallback {
override fun onError(error: TenMaxAdBeaconError) {
Log.e("BeaconSDK", "SDK Error: ${error.message}")
when (error.code) {
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.UNAUTHORIZED_LOCATION_PERMISSION -> {
showLocationPermissionAlert()
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.BLUETOOTH_DISABLED -> {
showBluetoothDisabledAlert()
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.BLUETOOTH_PERMISSION_DENIED -> {
showBluetoothPermissionAlert()
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.UNAUTHORIZED_NOTIFICATION_PERMISSION -> {
showNotificationPermissionAlert()
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.INTERNET_CONNECTION_UNAVAILABLE -> {
showOfflineMessage()
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.FREQUENCY_CAP_EXCEEDED -> {
// This is normal behavior - creative was blocked due to frequency limits
Log.d("BeaconSDK", "Creative blocked due to frequency capping")
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.CONFIGURATION_ERROR -> {
// Handle configuration issues
Log.e("BeaconSDK", "Configuration error: Check SDK initialization and configuration")
}
TenMaxAdBeaconError.TenMaxAdBeaconErrorType.NOT_FOUND_CREATIVE_DATA -> {
Log.d("BeaconSDK", "No creative content available for this beacon")
}
else -> {
showGenericErrorAlert(error.message)
}
}
}
// Implement other callback methods...
}
The SDK automatically persists user profile data across app restarts:
// First time - provide user data
val profile = ClientProfile(
phoneNumber = "0912345678",
email = "[email protected]",
appName = "YourApp"
)
// After app restart - data is automatically restored
// The SDK automatically loads previously saved profile data
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
// Correct implementation for advertising ID retrieval
suspend fun getAdvertisingIdSafely(context: Context): String? {
return withContext(Dispatchers.IO) {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
// Check if user has limited ad tracking
if (adInfo.isLimitAdTrackingEnabled) {
// User has opted out of ad tracking - do not use advertising ID
Log.d("AdID", "User has limited ad tracking - not using advertising ID")
null
} else {
// User allows ad tracking - safe to use advertising ID
Log.d("AdID", "Using advertising ID: ${adInfo.id}")
adInfo.id
}
} catch (e: Exception) {
Log.e("AdID", "Failed to get advertising ID: ${e.message}")
null
}
}
}
Important: Always respect user privacy preferences. When isLimitAdTrackingEnabled
returns true
, do not use the advertising ID for tracking purposes.
- Android: 6.0 (API level 23) or later
- Kotlin: 1.8.0 or later
- Compile SDK: 33 or later
- Target SDK: 33 or later
- Bluetooth: Bluetooth 4.0 (Bluetooth Low Energy) or later
- Location Services: Required for beacon detection
- Background App Refresh: Recommended for optimal background scanning
- Ensure location permissions are granted
- Verify Bluetooth is enabled
- Test on physical device (simulator limitations)
- Check beacon configuration in SDK settings
- Confirm notification permissions are granted
- Verify app is not in Do Not Disturb mode
- Check notification settings in Android Settings
- Check if device has network connectivity
- Clean build folder (Build > Clean Project)
- Update to latest Android Studio version
- Verify AAR file is properly included in libs directory
- Battery Usage: Beacon scanning uses Bluetooth and location services, which can impact battery life
- Frequency Capping: The SDK automatically implements frequency capping to prevent excessive notifications
- Network Usage: Creative content is fetched from CDN only when beacons are detected
- Beacon Deduplication: The SDK prevents duplicate beacon processing within 30-second intervals
Android publisher should provide the information that data their apps collect, including the data collected by third-party SDKs. For your convenience, TenMax SDK provides the information on its data collection in the Data Collection Survey for TenMax SDK.
If you encounter any issues using the TenMax Beacon SDK, please contact us at [email protected]. We will assist you as soon as possible.
For technical support or questions:
- Email: [email protected]
- Demo Code: Check the implementation examples in
sdkdemo
directory
For requests to delete the privacy data linked to users, please submit the request via User Data Deletion Notice Form.
TenMax