The Truemetrics SDK provides advanced sensor data collection and analytics capabilities for Android
applications. This guide covers all aspects of integrating and using the SDK effectively.
Table of Contents
Installation
Add the following to your root-level build.gradle file (or settings.gradle):
repositories {
maven {
url "https://github.com/TRUE-Metrics-io/truemetrics_android_SDK_p_maven/raw/"
}
}
Then add the dependency to your app-level build.gradle:
dependencies {
implementation 'io.truemetrics:truemetricssdk:1.5.1'
}
Quick Start
1. Initialize the SDK in your Application class
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val config = SdkConfiguration.Builder("YOUR_API_KEY").build()
TruemetricsSdk.init(this, config)
}
}
2. Start and stop recordings
val sdk = TruemetricsSdk.getInstance()
// Start recording sensor data
sdk.startRecording()
// Stop recording
sdk.stopRecording()
// Check recording status
if (sdk.isRecordingInProgress()) {
// Recording is active
}
Configuration
The SDK is configured using the SdkConfiguration.Builder class.
Basic Configuration
val config = SdkConfiguration.Builder("YOUR_API_KEY").build()
Custom Foreground Notification
Customize the notification shown when the SDK is running as a foreground service using the SDK’s notification channel:
class CustomNotificationFactory : ForegroundNotificationFactory {
override fun createNotification(context: Context): Notification {
// Use SDK's notification channel ID
return NotificationCompat.Builder(context, "FOREGROUND_SERVICE")
.setContentTitle("Truemetrics SDK Running")
.setContentText("Collecting sensor data...")
.setSmallIcon(R.drawable.ic_sensors)
.setOngoing(true)
.build()
}
}
val config = SdkConfiguration.Builder("YOUR_API_KEY")
.foregroundNotificationFactory(CustomNotificationFactory())
.build()
Core Features
Recording Management
val sdk = TruemetricsSdk.getInstance()
// Start recording
sdk.startRecording()
// Stop recording
sdk.stopRecording()
// Check recording status
val isRecording = sdk.isRecordingInProgress()
val isStopped = sdk.isRecordingStopped()
// Get recording start time
val startTime = sdk.getRecordingStartTime() // Returns timestamp in milliseconds
Device ID
Get the unique device identifier (available after initialization):
val deviceId = sdk.getDeviceId()
// Or get from Status
sdk.observeSdkStatus().collect { status ->
when (status) {
is Status.Initialized -> println("Device ID: ${status.deviceId}")
is Status.RecordingInProgress -> println("Device ID: ${status.deviceId}")
else -> {}
}
}
The device ID may rotate automatically based on a server-configured TTL. When expired, a new ID is generated transparently on the next initialization.
Log standardized delivery/pickup event metadata:
import io.truemetrics.truemetricssdk.metadata.StandardMetadata
sdk.logMetadata(StandardMetadata(
eventTime = "2025-01-15T14:30:00Z",
eventType = "delivery_successful",
deliveryId = "parcel_123",
tourId = "tour_456",
waypointLatitude = "52.5200",
waypointLongitude = "13.4050",
referenceLatitude = "52.5201",
referenceLongitude = "13.4051",
address = "Pflanzstrasse 5, 10762 Berlin",
extra = mapOf("type_vehicle" to "car")
))
The map-based logMetadata(Map<String, String>) overload is deprecated. Use logMetadata(StandardMetadata) instead.
For advanced metadata with templates and tags, see Metadata Guide.
Sensor Management
// Enable/disable all sensors
sdk.setAllSensorsEnabled(true)
// Check if sensors are enabled
val enabled = sdk.getAllSensorsEnabled()
// Observe available sensors
sdk.sensorInfo.collect { sensors ->
sensors.forEach { sensor ->
println("Sensor: ${sensor.sensorName}, Status: ${sensor.sensorStatus}")
println("Frequency: ${sensor.frequency} Hz")
if (sensor.missingPermissions.isNotEmpty()) {
println("Missing permissions: ${sensor.missingPermissions}")
}
}
}
Complete Cleanup
Warning: After calling deinitialize(), the SDK instance becomes unusable. You must call
init() again to use the SDK.
SDK Status
Monitor the SDK state using observeSdkStatus():
sdk.observeSdkStatus().collect { status ->
when (status) {
is Status.Uninitialized -> {
// SDK not yet initialized
}
is Status.Initializing -> {
// SDK is fetching config from server
// Retries automatically on network errors or server 5xx
}
is Status.Initialized -> {
// SDK initialized, deviceId available
val deviceId = status.deviceId
}
is Status.RecordingInProgress -> {
// Recording is active
val deviceId = status.deviceId
}
is Status.RecordingStopped -> {
// Recording has stopped
}
is Status.DelayedStart -> {
// Waiting for auto-start delay
val deviceId = status.deviceId
val delayMs = status.delayMs
}
is Status.TrafficLimitReached -> {
// Traffic limit reached
}
is Status.ReadingsDatabaseFull -> {
// Device storage full
}
is Status.Error -> {
// SDK encountered an error
val errorCode = status.errorCode
val message = status.message
}
is Status.AskForPermissions -> {
// SDK requesting permissions
val permissions = status.permissions
}
}
}
Error Codes
| Error Code | Description |
|---|
| AUTHENTICATION_ERROR | API key is not valid, expired or revoked |
| UPLOAD_ERROR | Recordings couldn’t be uploaded after exhausting all attempts |
| STORAGE_FULL | Device storage is full which prevents saving sensor readings |
| MISSING_NOTIFICATION_PERMISSION | Notification permission not granted, foreground service cannot start |
| CONFIG_ERROR | Configuration error |
| TRAFFIC_USED_UP | All allotted traffic has been used |
| SENSORS_NOT_WORKING | Some sensors are not working. Check if permissions are missing |
Statistics API
Upload Statistics
Monitor upload health:
val uploadStats = sdk.getUploadStatistics()
if (uploadStats != null) {
println("Successful uploads: ${uploadStats.successfulUploadsCount}")
println("Last upload: ${uploadStats.lastSuccessfulUploadTimestamp}")
}
Sensor Statistics
Get detailed sensor data quality:
val sensorStats = sdk.getSensorStatistics()
sensorStats?.forEach { stat ->
println("Sensor: ${stat.sensorName}")
println("Configured: ${stat.configuredFrequencyHz} Hz")
println("Actual: ${stat.actualFrequencyHz} Hz")
println("Quality: ${stat.quality}")
}
Quality levels:
- EXCELLENT: 95-100% of configured frequency
- GOOD: 80-95%
- POOR: 50-80%
- BAD: less than 50%
- UNKNOWN: no data or not recording
Best Practices
1. Initialization
- Always initialize in your
Application class
- Use the application context, not activity context
- Initialize once and reuse the singleton instance
2. Error Handling
sdk.observeSdkStatus().collect { status ->
when (status) {
is Status.Error -> {
Log.e("SDK", "Error: ${status.errorCode} - ${status.message}")
}
is Status.ReadingsDatabaseFull -> {
Log.w("SDK", "Storage full")
}
else -> {}
}
}
3. Monitoring SDK Health
fun checkSdkHealth() {
val uploadStats = sdk.getUploadStatistics()
if (uploadStats != null && uploadStats.successfulUploadsCount == 0) {
Log.w("SDK", "No successful uploads yet")
}
val sensorStats = sdk.getSensorStatistics()
sensorStats?.filter { it.quality == SensorDataQuality.BAD }?.forEach {
Log.w("SDK", "Bad quality for: ${it.sensorName}")
}
}
API Reference
TruemetricsSdk
| Method | Return Type | Description |
|---|
init(context, config) | TruemetricsSdk | Initialize SDK (static) |
getInstance() | TruemetricsSdk | Get singleton instance (static) |
startRecording() | Unit | Start sensor recording |
stopRecording() | Unit | Stop sensor recording |
isRecordingInProgress() | Boolean | Check if recording active |
isRecordingStopped() | Boolean | Check if recording stopped |
getRecordingStartTime() | Long | Get recording start timestamp |
getDeviceId() | String? | Get unique device identifier |
logMetadata(standardMetadata) | Unit | Log standardized event metadata |
logMetadata(payload) (deprecated) | Unit | Log custom metadata map |
setAllSensorsEnabled(enabled) | Unit | Enable/disable all sensors |
getAllSensorsEnabled() | Boolean | Get sensor enable status |
getActiveConfig() | Configuration? | Get active backend configuration |
getUploadStatistics() | UploadStatistics? | Get upload stats |
getSensorStatistics() | List<SensorStatistics>? | Get sensor stats |
deinitialize() | Unit | Shutdown SDK completely |
Observable Flows
| Property/Method | Type | Description |
|---|
sensorInfo | StateFlow<Iterable<SensorInfo>> | Available sensors info |
sdkStatus | StateFlow<Status> | SDK operational status |
observeSdkStatus() | Flow<Status> | Observe SDK status changes |
getActiveConfigFlow() | Flow<Configuration>? | Observe backend config changes |
Data Classes
UploadStatistics
| Property | Type | Description |
|---|
successfulUploadsCount | Int | Total successful uploads |
lastSuccessfulUploadTimestamp | Long? | Last upload timestamp |
SensorStatistics
| Property | Type | Description |
|---|
sensorName | SensorName | Sensor identifier |
configuredFrequencyHz | Float | Configured frequency |
actualFrequencyHz | Float | Actual measured frequency |
quality | SensorDataQuality | Quality assessment |
SensorInfo
| Property | Type | Description |
|---|
sensorName | SensorName | Sensor identifier |
sensorStatus | SensorStatus | Status: ON, OFF, or NA |
frequency | Float | Polling frequency in Hz |
missingPermissions | List<String> | Required permissions not granted |