From 0fbb5dd00e83a94f921e250aaa005b3fdd188980 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 24 Aug 2026 06:29:54 -0700 Subject: [PATCH] fix(android): normalize optional motion date ranges (#128734) --- .../ai/openclaw/app/node/MotionHandler.kt | 8 +-- .../ai/openclaw/app/node/MotionHandlerTest.kt | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/apps/android/app/src/main/java/ai/openclaw/app/node/MotionHandler.kt b/apps/android/app/src/main/java/ai/openclaw/app/node/MotionHandler.kt index 536ffd989ee8..737811446426 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/node/MotionHandler.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/node/MotionHandler.kt @@ -388,8 +388,8 @@ class MotionHandler private constructor( // one live classification sample for now. val limit = ((params["limit"] as? JsonPrimitive)?.content?.toIntOrNull() ?: 200).coerceIn(1, 1000) return MotionActivityRequest( - startISO = (params["startISO"] as? JsonPrimitive)?.content?.trim()?.ifEmpty { null }, - endISO = (params["endISO"] as? JsonPrimitive)?.content?.trim()?.ifEmpty { null }, + startISO = parseJsonString(params, "startISO")?.trim()?.ifEmpty { null }, + endISO = parseJsonString(params, "endISO")?.trim()?.ifEmpty { null }, limit = limit, ) } @@ -405,8 +405,8 @@ class MotionHandler private constructor( null } ?: return null return MotionPedometerRequest( - startISO = (params["startISO"] as? JsonPrimitive)?.content?.trim()?.ifEmpty { null }, - endISO = (params["endISO"] as? JsonPrimitive)?.content?.trim()?.ifEmpty { null }, + startISO = parseJsonString(params, "startISO")?.trim()?.ifEmpty { null }, + endISO = parseJsonString(params, "endISO")?.trim()?.ifEmpty { null }, ) } diff --git a/apps/android/app/src/test/java/ai/openclaw/app/node/MotionHandlerTest.kt b/apps/android/app/src/test/java/ai/openclaw/app/node/MotionHandlerTest.kt index ff0117f91a94..552a6bc4378c 100644 --- a/apps/android/app/src/test/java/ai/openclaw/app/node/MotionHandlerTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/app/node/MotionHandlerTest.kt @@ -9,6 +9,7 @@ import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Test @@ -73,6 +74,57 @@ class MotionHandlerTest : NodeHandlerRobolectricTest() { ) } + @Test + fun handleMotionActivity_treatsJsonNullRangeAsAbsent() = + runTest { + val dataSource = FakeMotionDataSource(hasPermission = true) + val handler = MotionHandler.forTesting(appContext(), dataSource) + + val result = handler.handleMotionActivity("""{"startISO":null,"endISO":null,"limit":null}""") + + assertTrue(result.ok) + assertNull(dataSource.lastActivityRequest?.startISO) + assertNull(dataSource.lastActivityRequest?.endISO) + assertEquals(200, dataSource.lastActivityRequest?.limit) + } + + @Test + fun handleMotionPedometer_treatsJsonNullRangeAsAbsent() = + runTest { + val dataSource = FakeMotionDataSource(hasPermission = true) + val handler = MotionHandler.forTesting(appContext(), dataSource) + + val result = handler.handleMotionPedometer("""{"startISO":null,"endISO":null}""") + + assertTrue(result.ok) + assertNull(dataSource.lastPedometerRequest?.startISO) + assertNull(dataSource.lastPedometerRequest?.endISO) + val payload = Json.parseToJsonElement(result.payloadJson ?: error("missing payload")).jsonObject + assertEquals( + 1234, + payload + .getValue("steps") + .jsonPrimitive.content + .toInt(), + ) + } + + @Test + fun motionRangeRequests_preserveLiteralNullStringsAndLimitBounds() = + runTest { + val dataSource = FakeMotionDataSource(hasPermission = true) + val handler = MotionHandler.forTesting(appContext(), dataSource) + + assertTrue(handler.handleMotionActivity("""{"startISO":" null ","endISO":"null","limit":2000}""").ok) + assertEquals("null", dataSource.lastActivityRequest?.startISO) + assertEquals("null", dataSource.lastActivityRequest?.endISO) + assertEquals(1000, dataSource.lastActivityRequest?.limit) + + assertTrue(handler.handleMotionPedometer("""{"startISO":"null","endISO":" null "}""").ok) + assertEquals("null", dataSource.lastPedometerRequest?.startISO) + assertEquals("null", dataSource.lastPedometerRequest?.endISO) + } + @Test fun handleMotionPedometer_mapsRangeUnsupportedError() = runTest { @@ -161,6 +213,9 @@ private class FakeMotionDataSource( private val activityError: Throwable? = null, private val pedometerError: Throwable? = null, ) : MotionDataSource { + var lastActivityRequest: MotionActivityRequest? = null + var lastPedometerRequest: MotionPedometerRequest? = null + override fun isActivityAvailable(context: Context): Boolean = activityAvailable override fun isPedometerAvailable(context: Context): Boolean = pedometerAvailable @@ -171,6 +226,7 @@ private class FakeMotionDataSource( context: Context, request: MotionActivityRequest, ): MotionActivityRecord { + lastActivityRequest = request activityError?.let { throw it } return activityRecord } @@ -179,6 +235,7 @@ private class FakeMotionDataSource( context: Context, request: MotionPedometerRequest, ): PedometerRecord { + lastPedometerRequest = request pedometerError?.let { throw it } return pedometerRecord }