Repository navigation
Android SBeacon Framework Event Handling
The SBeacon SDK provides an event-based system to notify your application of state changes, data arrivals, and real-time alerts.
Subscribe to SDK events by extending IBFHelper.EventsReceiver and overriding callback methods for the events you need.
import com.scope.ibeacons.IBFHelper
import com.scope.common.models.*
class MyFragment : Fragment() {
private val beaconEventsReceiver = object : IBFHelper.EventsReceiver() {
override fun onBeaconConnected(mac: String) {
Log.d("Beacon", "Connected: $mac")
}
override fun onBeaconDisconnected(mac: String) {
Log.d("Beacon", "Disconnected: $mac")
}
override fun onTripStatusChanged(status: String) {
Log.d("Trip", "Status changed: $status")
}
override fun onDrivingBehaviorEventsReceived(
beaconMac: String,
events: ArrayList<DrivingBehaviorEvent>
) {
Log.d("DB", "Received ${events.size} driving behavior events")
events.forEach { event ->
Log.d("DB", "${event.eventType}: ${event.averageMagnitude}g")
}
}
override fun onRealTimeDrivingBehaviorEvent(
beaconMac: String,
eventType: DrivingBehaviorType,
timestamp: Long
) {
Log.w("DB", "Real-time alert: $eventType")
showDrivingAlert(eventType)
}
}
override fun onResume() {
super.onResume()
IBFHelper.registerEventsReceiver(requireContext(), beaconEventsReceiver)
}
override fun onPause() {
super.onPause()
IBFHelper.unregisterEventsReceiver(requireContext(), beaconEventsReceiver)
}
}override fun onBeaconConnected(mac: String) {
// Beacon established BLE connection
}
override fun onBeaconDisconnected(mac: String) {
// Beacon lost BLE connection
}
override fun onBeaconPaired(name: String) {
// Beacon successfully paired
}
override fun onBeaconUnpaired(name: String) {
// Beacon was unpaired
}
override fun onBeaconUpdated(name: String) {
// Beacon data updated
}override fun onTripStatusChanged(status: String) {
// Trip lifecycle changed (IDLE, START, ACTIVE, END, TIMEOUT)
when (status) {
"IDLE" -> Log.d("Trip", "No active trip")
"START" -> Log.d("Trip", "Trip starting")
"ACTIVE" -> Log.d("Trip", "Trip in progress")
"END" -> Log.d("Trip", "Trip ending")
"TIMEOUT" -> Log.d("Trip", "Trip timed out")
}
}
override fun onUnsyncedDataCounts(
beaconMac: String,
tripCount: Int,
incidentCount: Int,
dbCount: Int
) {
// Number of unsynced records on the beacon
Log.d("Sync", "Pending: $tripCount trips, $incidentCount incidents, $dbCount DB events")
}
override fun onTripRecordsReceived(
beaconMac: String,
trips: ArrayList<TripRecord>
) {
// Stored trip records retrieved from beacon
trips.forEach { trip ->
val duration = (trip.endTimestamp - trip.startTimestamp) / 1000 / 60
Log.d("Trip", "Trip ${trip.recordIndex}: $duration minutes")
}
}
override fun onTripIncidentsReceived(
beaconMac: String,
incidents: ArrayList<IncidentRecord>
) {
// Stored collision/incident records
incidents.forEach { incident ->
Log.d("Incident", "Incident at ${Date(incident.timestamp)}")
}
}override fun onDrivingBehaviorEventsReceived(
beaconMac: String,
events: ArrayList<DrivingBehaviorEvent>
) {
// Stored driving behavior events (up to 80)
events.forEach { event ->
Log.d("DB", """
Type: ${event.eventType}
Duration: ${event.durationMs}ms
Avg G-force: ${event.averageMagnitude}g
Max G-force: ${event.maxMagnitude}g
""".trimIndent())
}
}
override fun onRealTimeDrivingBehaviorEvent(
beaconMac: String,
eventType: DrivingBehaviorType,
timestamp: Long
) {
// Real-time harsh driving alert (during active trip)
when (eventType) {
DrivingBehaviorType.HARSH_ACCELERATION -> {
showWarning("Hard acceleration detected!")
}
DrivingBehaviorType.HARSH_BRAKING -> {
showWarning("Hard braking detected!")
}
DrivingBehaviorType.HARSH_CORNERING -> {
showWarning("Sharp turn detected!")
}
DrivingBehaviorType.ACCIDENT -> {
triggerEmergencyAlert()
}
}
}
override fun onDrivingBehaviorConfigReceived(
beaconMac: String,
config: DrivingBehaviorConfig
) {
// Configuration settings received
Log.d("DB", "Enabled: ${config.enabled}")
Log.d("DB", "Corner threshold: ${config.corneringThreshold}mg")
Log.d("DB", "Brake threshold: ${config.brakingThreshold}mg")
Log.d("DB", "Accel threshold: ${config.accelerationThreshold}mg")
}
override fun onDrivingBehaviorEventsClearConfirmed(
beaconMac: String,
clearedIndex: Int
) {
// Event clear operation confirmed
if (clearedIndex == 0xFF) {
Log.d("DB", "All events cleared successfully")
} else {
Log.d("DB", "Event $clearedIndex cleared")
}
}override fun onAutoCalibrationReceived(mac: String) {
// Accelerometer calibration completed
Log.d("Calibration", "Beacon $mac calibration complete")
}
override fun onCalibrationResetReceived(mac: String) {
// Calibration was reset
Log.d("Calibration", "Calibration reset for $mac")
}
override fun onUpdateBeaconCalibrationStateReceived(
mac: String,
isCalibrated: Boolean
) {
// Calibration state update
if (isCalibrated) {
Log.d("Calibration", "Beacon is calibrated")
} else {
Log.d("Calibration", "Beacon needs calibration")
}
}override fun onEmergencyButtonPressed() {
// Physical emergency button on beacon was pressed
triggerEmergencyResponse()
}
override fun onAccidentThresholdReceived(mac: String, value: Int) {
// Accident detection threshold value
Log.d("Threshold", "Accident threshold: ${value}mg")
}| Callback Method | Parameters | Description |
|---|---|---|
onBeaconConnected |
mac: String |
Beacon BLE connected |
onBeaconDisconnected |
mac: String |
Beacon BLE disconnected |
onBeaconPaired |
name: String |
Beacon paired successfully |
onBeaconUnpaired |
name: String |
Beacon unpaired |
onBeaconUpdated |
name: String |
Beacon data updated |
onTripStatusChanged |
status: String |
Trip status changed |
onUnsyncedDataCounts |
beaconMac, tripCount, incidentCount, dbCount |
Pending upload counts |
onTripRecordsReceived |
beaconMac, trips |
Stored trip records |
onTripIncidentsReceived |
beaconMac, incidents |
Stored incident records |
onDrivingBehaviorEventsReceived |
beaconMac, events |
Stored DB events |
onRealTimeDrivingBehaviorEvent |
beaconMac, eventType, timestamp |
Real-time harsh driving |
onDrivingBehaviorConfigReceived |
beaconMac, config |
DB configuration |
onDrivingBehaviorEventsClearConfirmed |
beaconMac, clearedIndex |
Clear confirmation |
onAutoCalibrationReceived |
mac: String |
Calibration completed |
onCalibrationResetReceived |
mac: String |
Calibration reset |
onUpdateBeaconCalibrationStateReceived |
mac, isCalibrated |
Calibration state |
onEmergencyButtonPressed |
- | Emergency button pressed |
onAccidentThresholdReceived |
mac, value |
Accident threshold |
Always pair registration with the appropriate lifecycle methods:
// Fragment
override fun onResume() {
super.onResume()
IBFHelper.registerEventsReceiver(requireContext(), beaconEventsReceiver)
}
override fun onPause() {
super.onPause()
IBFHelper.unregisterEventsReceiver(requireContext(), beaconEventsReceiver)
}
// Activity
override fun onStart() {
super.onStart()
IBFHelper.registerEventsReceiver(this, beaconEventsReceiver)
}
override fun onStop() {
super.onStop()
IBFHelper.unregisterEventsReceiver(this, beaconEventsReceiver)
}Always register your receiver before requesting data from the beacon:
// CORRECT
override fun onResume() {
super.onResume()
IBFHelper.registerEventsReceiver(requireContext(), beaconEventsReceiver)
bleManager.requestDrivingBehaviorEvents(beaconMac)
}
// WRONG - may miss the response
override fun onResume() {
super.onResume()
bleManager.requestDrivingBehaviorEvents(beaconMac)
IBFHelper.registerEventsReceiver(requireContext(), beaconEventsReceiver) // Too late!
}You don't need to override all methods - only the ones you care about:
private val eventsReceiver = object : IBFHelper.EventsReceiver() {
override fun onTripStatusChanged(status: String) {
// Only handling trip status changes
updateTripUI(status)
}
}Event callbacks are delivered on the main thread, so you can update UI directly:
override fun onRealTimeDrivingBehaviorEvent(
beaconMac: String,
eventType: DrivingBehaviorType,
timestamp: Long
) {
// Safe to update UI directly
binding.alertText.text = "Alert: $eventType"
binding.alertText.visibility = View.VISIBLE
}For heavy processing, move to background thread:
override fun onDrivingBehaviorEventsReceived(
beaconMac: String,
events: ArrayList<DrivingBehaviorEvent>
) {
lifecycleScope.launch(Dispatchers.IO) {
// Process events on background thread
val processed = processEvents(events)
withContext(Dispatchers.Main) {
// Update UI on main thread
updateUI(processed)
}
}
}When managing multiple beacons, filter events by MAC address:
private val currentBeaconMac = "AA:BB:CC:DD:EE:FF"
override fun onDrivingBehaviorEventsReceived(
beaconMac: String,
events: ArrayList<DrivingBehaviorEvent>
) {
if (beaconMac == currentBeaconMac) {
// Only process events from our beacon
processEvents(events)
}
}class BeaconMonitorFragment : Fragment() {
private var _binding: FragmentBeaconMonitorBinding? = null
private val binding get() = _binding!!
private val bleManager by lazy { BLEManager.retrieveInstance(requireContext()) }
private var currentBeaconMac: String? = null
private var lastTripStatus: String? = null
private val beaconEventsReceiver = object : IBFHelper.EventsReceiver() {
override fun onBeaconConnected(mac: String) {
binding.connectionStatus.text = "Connected"
binding.connectionStatus.setTextColor(Color.GREEN)
}
override fun onBeaconDisconnected(mac: String) {
binding.connectionStatus.text = "Disconnected"
binding.connectionStatus.setTextColor(Color.RED)
}
override fun onTripStatusChanged(status: String) {
binding.tripStatus.text = "Trip: $status"
when (status) {
"ACTIVE" -> startTripMonitoring()
"END" -> stopTripMonitoring()
"IDLE" -> {
// Only sync when transitioning from END to IDLE
if (lastTripStatus == "END") {
syncTripData()
}
}
}
lastTripStatus = status
}
override fun onUnsyncedDataCounts(
beaconMac: String,
tripCount: Int,
incidentCount: Int,
dbCount: Int
) {
binding.unsyncedCount.text =
"Unsynced: $tripCount trips, $incidentCount incidents, $dbCount events"
if (tripCount > 0 || incidentCount > 0 || dbCount > 0) {
binding.syncButton.isEnabled = true
}
}
override fun onDrivingBehaviorEventsReceived(
beaconMac: String,
events: ArrayList<DrivingBehaviorEvent>
) {
lifecycleScope.launch {
// Upload to backend
uploadEventsToServer(events)
// Clear after successful upload
bleManager.clearAllDrivingBehaviorEvents(beaconMac)
}
}
override fun onRealTimeDrivingBehaviorEvent(
beaconMac: String,
eventType: DrivingBehaviorType,
timestamp: Long
) {
val message = when (eventType) {
DrivingBehaviorType.HARSH_ACCELERATION -> "Hard Acceleration!"
DrivingBehaviorType.HARSH_BRAKING -> "Hard Braking!"
DrivingBehaviorType.HARSH_CORNERING -> "Sharp Turn!"
DrivingBehaviorType.ACCIDENT -> "COLLISION DETECTED!"
}
showAlert(message, eventType == DrivingBehaviorType.ACCIDENT)
}
override fun onUpdateBeaconCalibrationStateReceived(
mac: String,
isCalibrated: Boolean
) {
if (isCalibrated) {
binding.calibrationStatus.text = "Calibrated"
binding.calibrateButton.visibility = View.GONE
binding.enableDbButton.isEnabled = true
} else {
binding.calibrationStatus.text = "Not Calibrated"
binding.calibrateButton.visibility = View.VISIBLE
binding.enableDbButton.isEnabled = false
}
}
override fun onAutoCalibrationReceived(mac: String) {
Snackbar.make(binding.root, "Calibration completed!", Snackbar.LENGTH_LONG).show()
}
override fun onDrivingBehaviorEventsClearConfirmed(
beaconMac: String,
clearedIndex: Int
) {
Snackbar.make(binding.root, "Events cleared from beacon", Snackbar.LENGTH_SHORT).show()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentBeaconMonitorBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
currentBeaconMac = arguments?.getString("beaconMac")
setupButtons()
}
override fun onResume() {
super.onResume()
IBFHelper.registerEventsReceiver(requireContext(), beaconEventsReceiver)
// Request current state
currentBeaconMac?.let { mac ->
bleManager.requestTripInformation(mac)
bleManager.requestAccelerometerConfig(mac)
bleManager.requestDrivingBehaviorConfig(mac)
}
}
override fun onPause() {
super.onPause()
IBFHelper.unregisterEventsReceiver(requireContext(), beaconEventsReceiver)
}
private fun setupButtons() {
binding.calibrateButton.setOnClickListener {
currentBeaconMac?.let { bleManager.requestAccelerometerCalibration(it) }
}
binding.enableDbButton.setOnClickListener {
currentBeaconMac?.let { bleManager.enableDrivingBehavior(it) }
}
binding.syncButton.setOnClickListener {
syncTripData()
}
}
private fun syncTripData() {
currentBeaconMac?.let { mac ->
bleManager.requestTripRecords(mac)
bleManager.requestTripIncidents(mac)
bleManager.requestDrivingBehaviorEvents(mac)
}
}
private fun startTripMonitoring() {
// Enable real-time monitoring UI
}
private fun stopTripMonitoring() {
// Disable real-time monitoring UI
}
private suspend fun uploadEventsToServer(events: ArrayList<DrivingBehaviorEvent>) {
// Upload to your backend
}
private fun showAlert(message: String, isCritical: Boolean) {
val color = if (isCritical) Color.RED else Color.parseColor("#FF5722")
Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG)
.setBackgroundTint(color)
.show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}- Driving Behavior - Driving behavior event details
- Trip Management - Trip events and lifecycle
- Calibration - Calibration procedures
- Data Models - Event data structures
- API Reference - Request methods that trigger 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