I'm creating an android app which uses a telematics SDK to start recording driving activity when the SDK picks up that the user is driving. The SDK runs in the background, and has full access to location data in the background in order to do this.
I have created a class which extends the main tracking receiver class of the SDK and I'm overriding a specific function, called 'onStartTracking' which was successfully running when the app picks up that the user is in a vehicle and moving.
Now, in this class, I'm trying to invoke a service which will run in the background when the onStartTracking function runs. This service is called MeasurementService, and contains all of the code that handles the sensor readings.
My question is, how can I get this service to run as part of a normal kotlin class, as opposed to within an activity (once again, this is a background-triggered service and does not have any user interaction to kick it off).
My research into this shows me that it can only be done by creating the service through an Intent, which requires an activity. I've tried the below which I believe runs the intent as part of the currently foregrounded activity in the app (I haven't yet understood which activity that could be if the app is not actually active on the user's device) but I think something is going wrong because of the SuppressLint annotation on the function - I could be wrong though.
package com.breez.data.tracking
import android.content.Context
import android.content.Intent
import android.location.Location
import androidx.appcompat.app.AppCompatActivity
import android.app.Activity
import androidx.lifecycle.MutableLiveData
import com.breez.sensorservices.handlers.SensorViewHandler
import com.breez.sensorservices.helpers.MeasurementRepository
import com.breez.sensorservices.helpers.SensorViewModel
import com.breez.sensorservices.helpers.SensorRepository
import com.breez.sensorservices.helpers.viewmodels.MeasurementViewModel
import com.breez.sensorservices.services.MeasurementService
import com.breez.wearoslib.WearOsHandler
import com.google.android.material.internal.ContextUtils.getActivity
import com.raxeltelematics.android.tracking.data.sensors.models.Event
import com.raxeltelematics.v2.sdk.SpeedViolation
import com.raxeltelematics.v2.sdk.TrackingEventsReceiver
import com.raxeltelematics.v2.sdk.utils.ContextUtils.sendBroadcast
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalCoroutinesApi
import javax.inject.Inject
class TrackingReceiver: TrackingEventsReceiver() {
private var sensorViewHandler: SensorViewHandler? =null;
private var sensorRepo: SensorRepository? = null;
private var wearOSHandler = WearOsHandler();
private var measurementRepo: MeasurementRepository = MeasurementRepository(wearOSHandler);
private val viewModel: MeasurementViewModel = MeasurementViewModel(measurementRepo);
private var sensorHelper: SensorViewModel?=null;
private var activity: Activity? =null;
// binder to the MeasurementService
private var _measurementBinder: MutableLiveData<MeasurementService.MeasurementBinder?> = MutableLiveData()
val measurementBinder: MutableLiveData<MeasurementService.MeasurementBinder?>
get() = _measurementBinder
private val measurementService: MeasurementService = MeasurementService()
override fun onLocationChanged(context: Context, location: Location) {
// new location found
println("***LOCATION CHANGED***")
println(location.toString())
}
@SuppressLint("RestrictedApi")
override fun onStartTracking(context: Context) {
// tracking started
println("***TRACKING STARTED***")
sensorViewHandler = SensorViewHandler(context);
sensorRepo = SensorRepository(sensorViewHandler!!, wearOSHandler);
sensorHelper = SensorViewModel(sensorRepo!!);
val serviceIntent = Intent(context, MeasurementService::class.java)
val sensorViewModel = sensorHelper!!.getSensorView()
val folderName = MeasurementService.generateFolderName(MeasurementService.ENDLESS)
val wearOsSensors = sensorHelper!!.getSensorView().getWearOsToMeasure()
wearOsSensors?.let { sensors ->
sensorHelper!!.startWearOsMeasurement(
context,
folderName,
sensors
)
}
MeasurementService.addExtraToIntentBasic(
serviceIntent,
false, // false - Phone storage, true - Wearable storage
sensorViewModel.getSensorsToMeasure(), // phone sensors
sensorViewModel.gpsMeasurement, // GPS
folderName,
wearOsSensors // Wear Os sensors - can be null
)
activity = getActivity(context);
Intent(context, MeasurementService::class.java).also { intent ->
activity?.bindService(intent, viewModel.connectionMeasurement, Context.BIND_AUTO_CREATE)
println("%%%%Measurement Service Invoked%%%%")
}
}
override fun onStopTracking(context: Context) {
// tracking stopped
val intent = Intent(MeasurementService.STOP_SERVICE)
intent.putExtra(MeasurementService.USER, true)
activity?.sendBroadcast(intent)
println("***TRACKING STOPPED***")
}
override fun onSpeedViolation(context: Context, violation: SpeedViolation) {
// new speed violation
println("***SPEED VIOLATION***")
println(violation.toString())
}
override fun onNewEvents(context: Context, events: Array<Event>) {
// filter accidents from events
events.filter { it.type == "Accident" }
println("***OTHER EVENTS***" + events.contentDeepToString())
// events.filter { println(it.type)}
}
override fun onSdkDeprecated(context: Context) {
// deprecated sdk callback
println("***SDK DEPRECATED***")
}
}
The annotation is necessary though to use the getActivity function in order to create the intent.
Before adding in the code which is in the onStartTracking function, I only had the print statement (TRACKING STARTED) to show me that the tracking had started and it worked - as soon as I got in my car and started driving with the debugger on, it printed the statement to the debug console. It was also printing the 'LOCATION CHANGED statement every few seconds showing me that the sdk is working fine. Now that I've built in the rest of the code to invoke the service, it doesn't even print either of those lines, but it's also not throwing any errors which is strange.
Any suggestions?
Sidenote - I'm new to Android dev coming from a web dev background, any tips or advice would be greatly appreciated :)
Thanks!
No. It requires a
Context.Activityis aContext, but there are others. More directly, you are passed aContextinonStartTracking()that you could use forbindService(). Hopefully, this SDK is showing you how to do this (in particular, when to callunbindService()).