Workaround for TYPE_STEP_COUNTER onSensorChanged not triggered in background

30 Views Asked by At

TYPE_STEP_COUNTER onSensorChanged is not being triggered when in background. Found this doc that says, it should be done in either foreground or with foreground service https://developer.android.com/develop/sensors-and-location/sensors/sensors_overview#only-gather-sensor-data-in-the-foreground.

I have create Flutter custom plugin for accessing native code that solves MissingPluginException when called from background thread. But onSensorChanged not being triggered makes it unusable for when app is closed.

I have also checked all possible SO threads regarding this. Closest one is this Using TYPE_STEP_COUNTER in a background service? but the answer given TriggerEventListener is not working as onTrigger is not fired for type_step_counter.

class StepCounterAndroidPlugin: FlutterPlugin, MethodCallHandler, SensorEventListener {  
  private lateinit var channel : MethodChannel
  private lateinit var sensorManager: SensorManager
  private var stepSensor: Sensor? = null
  private var stepData: String = ""
  private lateinit var contextNeeded: Context

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
  channel = MethodChannel(flutterPluginBinding.binaryMessenger, "android_step_counter")
  channel.setMethodCallHandler(this)
  contextNeeded = flutterPluginBinding.applicationContext
  sensorManager = flutterPluginBinding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager
      stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
      stepSensor?.let {
          sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL)
      }
}

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
  when (call.method) {
      "getStepCount" -> result.success(stepData)      
      else -> result.notImplemented()
  }
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
  channel.setMethodCallHandler(null)
  sensorManager.unregisterListener(this)
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
  // Do nothing
}

override fun onSensorChanged(event: SensorEvent?) {
  event?.let {
      if (it.sensor.type == Sensor.TYPE_STEP_COUNTER) {
          val stepCount = it.values[0].toInt()            
          val timestampNanoseconds = (Date().time - SystemClock.elapsedRealtime()) * 1000000L + it.timestamp
          stepData = "{\"timestamp\":$timestampNanoseconds, \"steps\":$stepCount}"
      }
  }
}
}

The flutter side code is

final methodChannel = const MethodChannel('android_step_counter');

  // call native method to get step count from sensor manager
  @override
  Future<StepDataAndroid?> getStepCount() async {
    final stepData = await methodChannel.invokeMethod<String>('getStepCount');
    return handleJsonData(stepData);
  }

This code can be further used with flutter workmanager package for periodic call which works fine as long as app is not removed from recent apps or killed.

0

There are 0 best solutions below