erluxman
erluxman

Reputation: 19385

Run Service to collect sensor data only when user is moving

I wanted to track sensor data (accelerometer, gyro) when user is moving/ phone is not stationary.

Things I was able to do :

  1. Listen to sensor data using Sensor Listener
        sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
        
        val accelerometerSensor = 
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
    
        sensorManager.registerListener(
                accSensor,
                accelerometerSensor,
                SensorManager.SENSOR_DELAY_NORMAL
            )
  1. Run a foreground service that always keeps running in the background to track the sensors even when the app is swiped off from recent apps
serviceIntent = Intent(context, SensorService::class.java)
context.startForegroundService(serviceIntent)
  1. Start the service as soon as the app is rebooted using broadcast listeners which listen to Boot completed event.

But I was not able to:

  1. Stop sensor service that collects sensor data when the device stops moving.
  2. Start sensor (which is not running) when the user's device starts to move and collect sensor data.

What might be the way to receive the Motion start and Motion end notifications/callback from the system so that we can decide to start/end foreground services.

Upvotes: 8

Views: 969

Answers (2)

user3252344
user3252344

Reputation: 758

The thresholds for what is 'movement' depends on your application so there's not a lot of ready-made options (e.g. do you care if the user shakes their phone, or just if they move somewhere in a car for a few streets?).

There are sensor types which detect movement start/stop. See here. Whether or not they're good for your app depends on your app.

  1. Stop sensor:

TYPE_STATIONARY_DETECT fires an event when the user has not moved for 5 seconds. Listen for it, and use it to stop your service. If you want faster than that, you'll have to do it yourself.

  1. Start sensor

TYPE_MOVEMENT_DETECT is the opposite of stationary-detect, and fires when the user has been moving for 5 seconds. You could listen for this to start the service back up if the 5 second latency isn't a deal-breaker.

Type SIGNIFICANT_MOTION is also useable if you want your movement to wake the app from sleep, but I've never had much luck finding what counts as 'significant' for that sensor type.

If your movement isn't one of those two things - a 5-second sustained movement or whatever sets off the 'significant' flag - then you will need to implement the start and the stop yourself, because a sensor can't detect what movement to start on if it's not already started & detecting movement. For example I've used something like this pseudocode

class MotionSensorMediator:
    
    enum state=[waiting, active]

    def ctor():
        state = waiting

    def onSensorEvent(SensorEvent event):
        switch state:
             case waiting:
                  checkMovementThreshold(event)
                  break
             case active:
                  checkMovementThreshold(event)
                  sendToListener(event)
                  break

    def checkMovementThreshold(SensorEvent event):
        results = //some distance calculations
        if result > threshold && state == waiting:
             state = active
        else if result < threshold && state == active:
             state = waiting

Upvotes: 2

x00
x00

Reputation: 13833

Too big for a comment, so I'll post it as an answer. And it is in some way an answer, but without proofs, just my experience.

I'm in no way an expert in the Android. But I know a thing or two about sensors in general. And the main thing is that they are always active (if not turned off entirely), and that there is always some noise. So

  1. One can't say with 100% certainty is there any movement or not. Instead you need to collect all events, choose your thresholds and decide it yourself. (Probably you'd want to aggregate the measurements from several sensors to make that decision, and even to consider the history of measurements - e.g. if a user is in a car on a bumpy road the noise will be higher for a prolonged period).
  2. There is virtually no difference in energy consumption if the corresponding code will be executed by the OS, or by your service. But there is a lot more opportunities to customize the thresholds, the aggregation and other logic with your code. That's why I believe that OS keeps API to the bare minimum, and there is no such events as moving starts/moving stops.
  3. The only thing that drains energy above the minimum is your code. And no one else, but your code decides to annoy users with a constant stream of notifications or not.

So, think of aggregation strategy and thresholds, don't show notifications if thresholds are not passed, and make your code fast (so that it won't use too much energy). And your are done... more or less... because these decisions are not easy to make. A lot of apps from big companies fail to make them right in all the cases. Just install a couple of pedometer-apps and you'll see that they all will show different counts of steps.

Upvotes: 3

Related Questions