Repository navigation
Android SBeacon Framework Initialization
Aigars Mališevs edited this page Feb 2, 2026
·
3 revisions
SBeacon SDK v1.6.13
This guide covers SDK initialization, Application class setup, and permission handling.
Create an Application subclass to initialize the SDK:
package com.yourcompany.yourapp
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import com.scope.common.SharedConstants
import com.scope.common.models.ConfigType
import com.scope.ibeacons.BLEManager
import com.scope.ibeacons.util.iBFPrefUtil
import timber.log.Timber
class MyApp : Application() {
companion object {
lateinit var instance: MyApp
private set
}
override fun onCreate() {
super.onCreate()
instance = this
// Optional: Setup logging
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
// Optional: Track app lifecycle
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
// CRITICAL: Only initialize SDK after permissions are granted
if (allPermissionsGranted()) {
configureBLEFramework()
}
}
/**
* Configures the BLE Framework SDK for SBeacon communication.
* MUST be called only after all required permissions are granted.
*/
fun configureBLEFramework() {
Timber.i("Configuring BLE framework...")
// Configure SDK preferences
with(iBFPrefUtil.getInstance(applicationContext)) {
// Set to SBeacon-only mode
requiredBluetoothManagers = listOf("SBeaconManager")
// Set configuration type
saveString(SharedConstants.PREFERENCES_CONFIG_TYPE, ConfigType.BLE.name)
}
// Start foreground service (required for Android 8.0+ background scanning)
BLEManager.retrieveInstance(applicationContext).startForegroundBluetoothService()
}
private fun allPermissionsGranted(): Boolean {
// Implement permission check logic
// See Permission Handling section below
return false
}
}Important: Call
configureBLEFramework()only after all permissions are granted. The SDK will crash if permissions are missing.
| Setting | Value | Description |
|---|---|---|
requiredBluetoothManagers |
listOf("SBeaconManager") |
Configures SDK for SBeacon-only operation |
ConfigType |
ConfigType.BLE |
Sets the configuration mode |
startForegroundBluetoothService() |
- | Required for continuous BLE scanning on Android 8.0+ |
Create a utility to determine required permissions based on Android version:
package com.yourcompany.yourapp.utils
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
object PermissionsUtil {
/**
* Returns batches of permissions that haven't been granted yet.
* Permissions are batched for sequential requesting.
*/
fun getNotGrantedPermissions(context: Context): List<Array<String>> {
val batches = mutableListOf<Array<String>>()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { // Android 13+
// Batch 1: Fine location
if (!isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
}
// Batch 2: Bluetooth permissions
val bluetoothPerms = mutableListOf<String>()
if (!isPermissionGranted(context, Manifest.permission.BLUETOOTH_SCAN)) {
bluetoothPerms.add(Manifest.permission.BLUETOOTH_SCAN)
}
if (!isPermissionGranted(context, Manifest.permission.BLUETOOTH_CONNECT)) {
bluetoothPerms.add(Manifest.permission.BLUETOOTH_CONNECT)
}
if (bluetoothPerms.isNotEmpty()) {
batches.add(bluetoothPerms.toTypedArray())
}
// Batch 3: Background location
if (!isPermissionGranted(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
}
// Batch 4: Notifications
if (!isPermissionGranted(context, Manifest.permission.POST_NOTIFICATIONS)) {
batches.add(arrayOf(Manifest.permission.POST_NOTIFICATIONS))
}
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { // Android 12
if (!isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
}
val bluetoothPerms = mutableListOf<String>()
if (!isPermissionGranted(context, Manifest.permission.BLUETOOTH_SCAN)) {
bluetoothPerms.add(Manifest.permission.BLUETOOTH_SCAN)
}
if (!isPermissionGranted(context, Manifest.permission.BLUETOOTH_CONNECT)) {
bluetoothPerms.add(Manifest.permission.BLUETOOTH_CONNECT)
}
if (bluetoothPerms.isNotEmpty()) {
batches.add(bluetoothPerms.toTypedArray())
}
if (!isPermissionGranted(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
}
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { // Android 11
if (!isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
}
if (!isPermissionGranted(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
}
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> { // Android 10
val perms = mutableListOf<String>()
if (!isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
perms.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
if (!isPermissionGranted(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
if (perms.isNotEmpty()) {
batches.add(perms.toTypedArray())
}
}
else -> { // Android 8.1-9
if (!isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
batches.add(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
}
}
}
return batches
}
private fun isPermissionGranted(context: Context, permission: String): Boolean {
return ContextCompat.checkSelfPermission(context, permission) ==
PackageManager.PERMISSION_GRANTED
}
}Orchestrates sequential permission requests:
package com.yourcompany.yourapp.utils
import android.app.Activity
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ActivityCompat
class PermissionHandler(
private val activity: Activity,
private val launcher: ActivityResultLauncher<Array<String>>,
private val onAllGranted: () -> Unit,
private val onDenied: (Array<String>) -> Unit,
private val onPermanentlyDenied: (String) -> Unit
) {
private var permissionQueue: MutableList<Array<String>> = mutableListOf()
var isRequesting = false
private set
fun start(permissionBatches: List<Array<String>>) {
if (isRequesting) return
permissionQueue.clear()
permissionQueue.addAll(permissionBatches)
isRequesting = true
requestNextBatch()
}
private fun requestNextBatch() {
if (permissionQueue.isEmpty()) {
isRequesting = false
onAllGranted()
return
}
val batch = permissionQueue.removeAt(0)
launcher.launch(batch)
}
fun onResult(permissions: Array<String>, results: BooleanArray) {
val deniedPermissions = permissions.filterIndexed { index, _ ->
!results[index]
}.toTypedArray()
if (deniedPermissions.isEmpty()) {
requestNextBatch()
} else {
val permanentlyDenied = deniedPermissions.firstOrNull { permission ->
!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
}
isRequesting = false
if (permanentlyDenied != null) {
onPermanentlyDenied(permanentlyDenied)
} else {
onDenied(deniedPermissions)
}
}
}
}class BeaconsFragment : Fragment() {
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { results ->
permissionHandler.onResult(
results.keys.toTypedArray(),
results.values.toBooleanArray()
)
}
private val permissionHandler by lazy {
PermissionHandler(
activity = requireActivity(),
launcher = permissionLauncher,
onAllGranted = {
// Initialize SDK
(requireActivity().application as MyApp).configureBLEFramework()
startBeaconsScan()
},
onDenied = { deniedPermissions ->
showPermissionRationaleDialog(deniedPermissions)
},
onPermanentlyDenied = { permission ->
showGoToSettingsDialog(permission)
}
)
}
override fun onResume() {
super.onResume()
if (permissionHandler.isRequesting) return
val notGranted = PermissionsUtil.getNotGrantedPermissions(requireContext())
if (notGranted.isEmpty()) {
startBeaconsScan()
} else {
permissionHandler.start(notGranted)
}
}
}Why Sequential Requests? Android 11+ requires background location to be requested separately. Sequential requests avoid overwhelming users and allow providing context for each permission group.
- Beacon Scanning - Start discovering SBeacon devices
- Event Handling - Subscribe to SDK events
Consolidated SDK
SmartDrive
PortalApiBase
PortalApiClient
Wellness
SBeacon
- Overview
- Installation
- Initialization
- Beacon Scanning
- Beacon Pairing
- Trip Management
- Driving Behavior
- Calibration
- Event Handling
- Data Models
- API Reference
Consolidated SPM
SDApiManager
CCFrameworkOpen
SmartDrive
SBeacon