From b5bc01fac22025fc3f7c2e278fdd17e4b2b2fe3b Mon Sep 17 00:00:00 2001 From: David Madl Date: Sun, 24 May 2026 07:29:26 +0200 Subject: [PATCH] feat: also collect track position in ms, allows pause handling --- .../at/lockstep/player/ui/NowPlayingScreen.kt | 10 +++-- .../lockstep/player/util/RunDataCollector.kt | 41 +++++++++++++------ .../at/lockstep/player/util/RunDataStorage.kt | 23 ++++++++++- .../player/util/RunTrackDataSnapshot.kt | 2 +- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/at/lockstep/player/ui/NowPlayingScreen.kt b/app/src/main/java/at/lockstep/player/ui/NowPlayingScreen.kt index 356f54c..18612a4 100644 --- a/app/src/main/java/at/lockstep/player/ui/NowPlayingScreen.kt +++ b/app/src/main/java/at/lockstep/player/ui/NowPlayingScreen.kt @@ -232,13 +232,17 @@ fun NowPlayingRoute( LaunchedEffect(collectRunData, playback) { if (!collectRunData) { - collector.setAcceptSamples(false) + collector.setCollectingEnabled(false) return@LaunchedEffect } - val service = playback ?: return@LaunchedEffect + val service = playback ?: run { + collector.setCollectingEnabled(false) + return@LaunchedEffect + } + collector.setPlaybackPositionMsProvider { service.getPlaybackPositionMs() } + collector.setCollectingEnabled(true) var lastTrackId: String? = null service.uiState.collect { state -> - collector.setAcceptSamples(state.isPlaying) val trackId = state.currentTrackId if (trackId != null && trackId != lastTrackId) { collector.markSongStart() diff --git a/app/src/main/java/at/lockstep/player/util/RunDataCollector.kt b/app/src/main/java/at/lockstep/player/util/RunDataCollector.kt index ae0820c..2791855 100644 --- a/app/src/main/java/at/lockstep/player/util/RunDataCollector.kt +++ b/app/src/main/java/at/lockstep/player/util/RunDataCollector.kt @@ -26,7 +26,7 @@ class RunDataCollector( private val handlerThread = HandlerThread("RunDataCollect").apply { start() } private val handler = Handler(handlerThread.looper) - private val accelBuffer = mutableListOf() + private val accelBuffer = mutableListOf() private val gyroBuffer = mutableListOf() private val gpsBuffer = mutableListOf() @@ -34,7 +34,10 @@ class RunDataCollector( private var songStartElapsedRealtimeNanos: Long? = null @Volatile - private var acceptSamples = false + private var collectingEnabled = false + + @Volatile + private var playbackPositionMsProvider: () -> Long = { 0L } private var sensorsRegistered = false private var locationRegistered = false @@ -42,22 +45,30 @@ class RunDataCollector( private val sensorListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { - if (!acceptSamples) return + if (!collectingEnabled) return val timestamp = relativeTimestampNanos(event.timestamp) ?: return - val sample = - RunDataSample( - timestampNanos = timestamp, - values = floatArrayOf(event.values[0], event.values[1], event.values[2]), - ) when (event.sensor.type) { - Sensor.TYPE_ACCELEROMETER -> + Sensor.TYPE_ACCELEROMETER -> { + val sample = + RunAccelSample( + timestampNanos = timestamp, + positionMs = playbackPositionMsProvider(), + values = floatArrayOf(event.values[0], event.values[1], event.values[2]), + ) synchronized(accelBuffer) { accelBuffer.add(sample) } - Sensor.TYPE_GYROSCOPE -> + } + Sensor.TYPE_GYROSCOPE -> { + val sample = + RunDataSample( + timestampNanos = timestamp, + values = floatArrayOf(event.values[0], event.values[1], event.values[2]), + ) synchronized(gyroBuffer) { gyroBuffer.add(sample) } + } } } @@ -69,7 +80,7 @@ class RunDataCollector( private val locationListener = LocationListener { location -> - if (!acceptSamples) return@LocationListener + if (!collectingEnabled) return@LocationListener recordGpsLocation(location) } @@ -148,8 +159,12 @@ class RunDataCollector( songStartElapsedRealtimeNanos = null } - fun setAcceptSamples(accept: Boolean) { - acceptSamples = accept + fun setCollectingEnabled(enabled: Boolean) { + collectingEnabled = enabled + } + + fun setPlaybackPositionMsProvider(provider: () -> Long) { + playbackPositionMsProvider = provider } fun snapshotAndClear(): RunTrackDataSnapshot = diff --git a/app/src/main/java/at/lockstep/player/util/RunDataStorage.kt b/app/src/main/java/at/lockstep/player/util/RunDataStorage.kt index 93105c9..08515b2 100644 --- a/app/src/main/java/at/lockstep/player/util/RunDataStorage.kt +++ b/app/src/main/java/at/lockstep/player/util/RunDataStorage.kt @@ -49,7 +49,7 @@ object RunDataStorage { val jsonString = JSONObject() .apply { - put("data", samplesToJsonArray(snapshot.accelerometer)) + put("data", accelToJsonArray(snapshot.accelerometer)) put("gyro", samplesToJsonArray(snapshot.gyroscope)) put("gps", gpsToJsonArray(snapshot.gps)) put("meta", metaContentUri) @@ -65,6 +65,27 @@ object RunDataStorage { } } + private fun accelToJsonArray(samples: List): JSONArray { + val array = JSONArray() + for (sample in samples) { + array.put( + JSONObject().apply { + put("timestamp", sample.timestampNanos) + put("positionMs", sample.positionMs) + put( + "values", + JSONArray().apply { + for (v in sample.values) { + put(v.toDouble()) + } + }, + ) + }, + ) + } + return array + } + private fun samplesToJsonArray(samples: List): JSONArray { val array = JSONArray() for (sample in samples) { diff --git a/app/src/main/java/at/lockstep/player/util/RunTrackDataSnapshot.kt b/app/src/main/java/at/lockstep/player/util/RunTrackDataSnapshot.kt index 5619738..bff6d6c 100644 --- a/app/src/main/java/at/lockstep/player/util/RunTrackDataSnapshot.kt +++ b/app/src/main/java/at/lockstep/player/util/RunTrackDataSnapshot.kt @@ -8,7 +8,7 @@ data class RunGpsSample( ) data class RunTrackDataSnapshot( - val accelerometer: List, + val accelerometer: List, val gyroscope: List, val gps: List, ) {