Starting a service that records sensor data in Kotlin Android App

568 Views Asked by At

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!

1

There are 1 best solutions below

0
CommonsWare On

which requires an activity

No. It requires a Context. Activity is a Context, but there are others. More directly, you are passed a Context in onStartTracking() that you could use for bindService(). Hopefully, this SDK is showing you how to do this (in particular, when to call unbindService()).