Driver Behaviour¶
The Driver Behaviour feature enables the analysis and scoring of a driver's behavior during a trip, identifying risky driving patterns and summarizing them with safety scores. This feature tracks both real-time and session-level driving events, such as harsh braking, cornering, or ignoring traffic signs, and evaluates overall risk using multiple criteria.
This data can be used to offer user feedback, identify unsafe habits, and assess safety levels over time. All information is processed using on-device sensor data (via the configured DataSource) and optionally matched to the road network if useMapMatchedPosition is enabled.
Starting and Stopping Analysis¶
To use the Driver Behaviour module, you first need to create a data source and then produce a DriverBehaviour instance using the factory method. The session is started using startAnalysis() and closed using stopAnalysis(), which returns a DriverBehaviourAnalysis instance representing the complete analysis.
- Kotlin
- Java
// Kotlin
val dataSource = DataSourceFactory.produceLive()
val driverBehaviour = DriverBehaviour.produce(dataSource!!, useMapMatchedPosition = true)
val started = driverBehaviour?.startAnalysis() ?: false
if (!started) {
Log.e("DriverBehaviour", "Failed to start analysis")
return
}
// ... after some driving
val result = driverBehaviour.stopAnalysis()
if (result?.valid != true) {
Log.w("DriverBehaviour", "The analysis is invalid and cannot be used")
return
}
// Process the analysis result
// processAnalysisResult(result)
// Java
DataSource dataSource = DataSourceFactory.produceLive();
DriverBehaviour driverBehaviour = DriverBehaviour.produce(dataSource, true);
boolean started = false;
if (driverBehaviour != null) {
started = driverBehaviour.startAnalysis();
}
if (!started) {
Log.e("DriverBehaviour", "Failed to start analysis");
return;
}
// ... after some driving
DriverBehaviourAnalysis result = driverBehaviour.stopAnalysis();
if (result == null || !result.getValid()) {
Log.w("DriverBehaviour", "The analysis is invalid and cannot be used");
return;
}
// Process the analysis result
// processAnalysisResult(result);
🚨 DANGER
All
DriverBehaviourAnalysisinstances expose avalidproperty to determine whether the analysis is valid. Always verify this property before accessing or relying on the data it contains.
Inspecting a Driving Session¶
The result returned by stopAnalysis() (or via lastAnalysis) contains aggregate and detailed information on the trip:
- Kotlin
- Java
// Kotlin
if (result?.valid != true) {
Log.w("DriverBehaviour", "The analysis is invalid and cannot be used")
return
}
val startTime = result.startTime // UnlTime object
val finishTime = result.finishTime // UnlTime object
val distance = result.kilometersDriven // Double
val drivingDuration = result.minutesDriven // Double
val speedingTime = result.minutesSpeeding // Double
val totalElapsedTime = result.minutesTotalElapsed // Double
val tailgatingTime = result.minutesTailgating // Double
// Java
if (result == null || !result.getValid()) {
Log.w("DriverBehaviour", "The analysis is invalid and cannot be used");
return;
}
UnlTime startTime = result.getStartTime(); // UnlTime object
UnlTime finishTime = result.getFinishTime(); // UnlTime object
double distance = result.getKilometersDriven(); // Double
double drivingDuration = result.getMinutesDriven(); // Double
double speedingTime = result.getMinutesSpeeding(); // Double
double totalElapsedTime = result.getMinutesTotalElapsed(); // Double
double tailgatingTime = result.getMinutesTailgating(); // Double
The session also includes risk scores:
- Kotlin
- Java
// Kotlin
val scores = result.drivingScores
if (scores == null) {
Log.w("DriverBehaviour", "No driving scores available")
return
}
val speedRisk = scores.speedAverageRiskScore
val speedVariableRisk = scores.speedVariableRiskScore
val harshAccelerationRisk = scores.harshAccelerationScore
val brakingRisk = scores.harshBrakingScore
val swervingRisk = scores.swervingScore
val corneringRisk = scores.corneringScore
val tailgatingRisk = scores.tailgatingScore
val ignoredSignsRisk = scores.ignoredStopSignsScore
val fatigue = scores.fatigueScore
val overallScore = scores.aggregateScore
// Java
DrivingScores scores = result.getDrivingScores();
if (scores == null) {
Log.w("DriverBehaviour", "No driving scores available");
return;
}
double speedRisk = scores.getSpeedAverageRiskScore();
double speedVariableRisk = scores.getSpeedVariableRiskScore();
double harshAccelerationRisk = scores.getHarshAccelerationScore();
double brakingRisk = scores.getHarshBrakingScore();
double swervingRisk = scores.getSwervingScore();
double corneringRisk = scores.getCorneringScore();
double tailgatingRisk = scores.getTailgatingScore();
double ignoredSignsRisk = scores.getIgnoredStopSignsScore();
double fatigue = scores.getFatigueScore();
double overallScore = scores.getAggregateScore();
📝 INFO
Each score ranges from 0 (unsafe) to 100 (safe). A score of -1 means invalid or unavailable.
Inspecting Driving Events¶
Use the drivingEvents property of the session result to access discrete driving incidents that were detected:
- Kotlin
- Java
// Kotlin
val events = result.drivingEvents
events?.asArrayList()?.forEach { event ->
val time = event.time
val latitude = event.latitudeDeg
val longitude = event.longitudeDeg
val eventType = event.eventType
Log.d("DriverBehaviour",
"Event at $latitude, $longitude at ${time?.asLong()} with type $eventType")
}
// Java
DrivingEventList events = result.getDrivingEvents();
if (events != null) {
for (DrivingEvent event : events.asArrayList()) {
UnlTime time = event.getTime();
double latitude = event.getLatitudeDeg();
double longitude = event.getLongitudeDeg();
EDrivingEvent eventType = event.getEventType();
Log.d("DriverBehaviour",
"Event at " + latitude + ", " + longitude + " at " + (time != null ? time.asLong() : 0) + " with type " + eventType);
}
}
Event types are defined by the EDrivingEvent enum:
Driving Event Types¶
| Enum Value | Description |
|---|---|
NoEvent |
No event |
StartingTrip |
Starting a trip |
FinishingTrip |
Finishing a trip |
Resting |
Resting |
HarshAcceleration |
Harsh acceleration |
HarshBraking |
Harsh braking |
Cornering |
Cornering |
Swerving |
Swerving |
Tailgating |
Tailgating |
IgnoringSigns |
Ignoring traffic signs |
Real-time Feedback¶
If the analysis is ongoing, you can fetch real-time scores using:
- Kotlin
- Java
// Kotlin
val instantScores = driverBehaviour?.instantaneousScores
instantScores?.let { scores ->
val currentSpeedRisk = scores.speedAverageRiskScore
val currentBrakingRisk = scores.harshBrakingScore
val currentOverallScore = scores.aggregateScore
// Use scores for real-time feedback
updateUIWithScores(currentSpeedRisk, currentBrakingRisk, currentOverallScore)
}
// Java
DrivingScores instantScores = driverBehaviour != null ? driverBehaviour.getInstantaneousScores() : null;
if (instantScores != null) {
double currentSpeedRisk = instantScores.getSpeedAverageRiskScore();
double currentBrakingRisk = instantScores.getHarshBrakingScore();
double currentOverallScore = instantScores.getAggregateScore();
// Use scores for real-time feedback
updateUIWithScores(currentSpeedRisk, currentBrakingRisk, currentOverallScore);
}
These reflect the user's current behavior and are useful for immediate in-app feedback.
Stop Analysis and Get Last Analysis¶
To stop an ongoing analysis you can use:
- Kotlin
- Java
// Kotlin
val analysis = driverBehaviour?.stopAnalysis()
if (analysis?.valid != true) {
Log.w("DriverBehaviour", "No valid analysis available")
return
}
// Process the completed analysis
processCompletedAnalysis(analysis)
// Java
DriverBehaviourAnalysis analysis = driverBehaviour != null ? driverBehaviour.stopAnalysis() : null;
if (analysis == null || !analysis.getValid()) {
Log.w("DriverBehaviour", "No valid analysis available");
return;
}
// Process the completed analysis
processCompletedAnalysis(analysis);
You can also retrieve the last completed analysis:
- Kotlin
- Java
// Kotlin
val lastAnalysis = driverBehaviour?.lastAnalysis
if (lastAnalysis?.valid != true) {
Log.w("DriverBehaviour", "No valid analysis available")
return
}
// Process the last analysis
processLastAnalysis(lastAnalysis)
// Java
DriverBehaviourAnalysis lastAnalysis = driverBehaviour != null ? driverBehaviour.getLastAnalysis() : null;
if (lastAnalysis == null || !lastAnalysis.getValid()) {
Log.w("DriverBehaviour", "No valid analysis available");
return;
}
// Process the last analysis
processLastAnalysis(lastAnalysis);
Retrieve Past Analyses¶
All completed sessions are stored locally and accessible via:
- Kotlin
- Java
// Kotlin
val pastSessions = driverBehaviour?.allDriverBehaviourAnalyses
pastSessions?.forEach { analysis ->
if (analysis.valid) {
// Process each valid analysis
processAnalysis(analysis)
}
}
// Java
List<DriverBehaviourAnalysis> pastSessions = driverBehaviour != null ? driverBehaviour.getAllDriverBehaviourAnalyses() : null;
if (pastSessions != null) {
for (DriverBehaviourAnalysis analysis : pastSessions) {
if (analysis.getValid()) {
// Process each valid analysis
processAnalysis(analysis);
}
}
}
You can also obtain a combined analysis over a time interval:
- Kotlin
- Java
// Kotlin
val startTime = UnlTime().apply {
longValue = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000) // 7 days ago
}
val endTime = UnlTime().apply {
longValue = System.currentTimeMillis() // Now
}
val combined = driverBehaviour?.getCombinedAnalysis(startTime, endTime)
if (combined?.valid == true) {
// Process combined analysis
processCombinedAnalysis(combined)
}
// Java
UnlTime startTime = new UnlTime();
startTime.setLongValue(System.currentTimeMillis() - (7L * 24 * 60 * 60 * 1000)); // 7 days ago
UnlTime endTime = new UnlTime();
endTime.setLongValue(System.currentTimeMillis()); // Now
DriverBehaviourAnalysis combined = driverBehaviour != null ? driverBehaviour.getCombinedAnalysis(startTime, endTime) : null;
if (combined != null && combined.getValid()) {
// Process combined analysis
processCombinedAnalysis(combined);
}
Analyses Storage Location¶
Driver behaviour analyses are stored locally on the device. Inside the app's directory, a folder named DriverBehaviour is created (at the same level as Data).
Data Cleanup¶
To save space or comply with privacy policies, older sessions can be erased:
- Kotlin
- Java
// Kotlin
val thirtyDaysAgo = UnlTime().apply {
longValue = System.currentTimeMillis() - (30L * 24 * 60 * 60 * 1000) // 30 days ago
}
driverBehaviour?.eraseAnalysesOlderThan(thirtyDaysAgo)
// Java
UnlTime thirtyDaysAgo = new UnlTime();
thirtyDaysAgo.setLongValue(System.currentTimeMillis() - (30L * 24 * 60 * 60 * 1000)); // 30 days ago
if (driverBehaviour != null) {
driverBehaviour.eraseAnalysesOlderThan(thirtyDaysAgo);
}
📝 INFO
Driver behaviour analysis requires a properly configured DataSource. Use
DataSourceFactory.produceLive()to create a live data source for real-time position updates. To ensure reliable results, make sure to start and stop the analysis appropriately and avoid frequent interruptions or overlapping sessions.
Memory Management¶
Remember to properly release resources when done:
- Kotlin
- Java
// Kotlin
driverBehaviour?.stopAnalysis()
driverBehaviour?.release()
dataSource?.release()
// Java
if (driverBehaviour != null) {
driverBehaviour.stopAnalysis();
driverBehaviour.release();
}
if (dataSource != null) {
dataSource.release();
}
Background Location¶
For driver behavior analysis while the app is in the background, ensure your app has the necessary location permissions:
- Kotlin
- Java
// Kotliln
private fun requestPermissions(activity: Activity): Boolean {
val permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION // For API 29+
)
return PermissionsHelper.requestPermissions(REQUEST_PERMISSIONS, activity, permissions)
}
// Java
private boolean requestPermissions(Activity activity) {
String[] permissions = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION // For API 29+
};
return PermissionsHelper.requestPermissions(REQUEST_PERMISSIONS, activity, permissions);
}