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.