refactor(android): share node capability and command manifest

This commit is contained in:
Ayaan Zaidi
2026-02-28 08:41:44 +05:30
committed by Ayaan Zaidi
parent f53ef73a2b
commit 3f06693e7d
3 changed files with 129 additions and 35 deletions

View File

@@ -7,7 +7,6 @@ import ai.openclaw.android.gateway.GatewayClientInfo
import ai.openclaw.android.gateway.GatewayConnectOptions
import ai.openclaw.android.gateway.GatewayEndpoint
import ai.openclaw.android.gateway.GatewayTlsParams
import ai.openclaw.android.protocol.OpenClawCapability
import ai.openclaw.android.LocationMode
import ai.openclaw.android.VoiceWakeMode
@@ -73,28 +72,18 @@ class ConnectionManager(
}
}
fun buildInvokeCommands(): List<String> =
InvokeCommandRegistry.advertisedCommands(
private fun runtimeFlags(): NodeRuntimeFlags =
NodeRuntimeFlags(
cameraEnabled = cameraEnabled(),
locationEnabled = locationMode() != LocationMode.Off,
smsAvailable = smsAvailable(),
voiceWakeEnabled = voiceWakeMode() != VoiceWakeMode.Off && hasRecordAudioPermission(),
debugBuild = BuildConfig.DEBUG,
)
fun buildCapabilities(): List<String> =
buildList {
add(OpenClawCapability.Canvas.rawValue)
add(OpenClawCapability.Screen.rawValue)
add(OpenClawCapability.Device.rawValue)
if (cameraEnabled()) add(OpenClawCapability.Camera.rawValue)
if (smsAvailable()) add(OpenClawCapability.Sms.rawValue)
if (voiceWakeMode() != VoiceWakeMode.Off && hasRecordAudioPermission()) {
add(OpenClawCapability.VoiceWake.rawValue)
}
if (locationMode() != LocationMode.Off) {
add(OpenClawCapability.Location.rawValue)
}
}
fun buildInvokeCommands(): List<String> = InvokeCommandRegistry.advertisedCommands(runtimeFlags())
fun buildCapabilities(): List<String> = InvokeCommandRegistry.advertisedCapabilities(runtimeFlags())
fun resolvedVersionName(): String {
val versionName = BuildConfig.VERSION_NAME.trim().ifEmpty { "dev" }

View File

@@ -3,12 +3,21 @@ package ai.openclaw.android.node
import ai.openclaw.android.protocol.OpenClawCanvasA2UICommand
import ai.openclaw.android.protocol.OpenClawCanvasCommand
import ai.openclaw.android.protocol.OpenClawCameraCommand
import ai.openclaw.android.protocol.OpenClawCapability
import ai.openclaw.android.protocol.OpenClawDeviceCommand
import ai.openclaw.android.protocol.OpenClawLocationCommand
import ai.openclaw.android.protocol.OpenClawNotificationsCommand
import ai.openclaw.android.protocol.OpenClawScreenCommand
import ai.openclaw.android.protocol.OpenClawSmsCommand
data class NodeRuntimeFlags(
val cameraEnabled: Boolean,
val locationEnabled: Boolean,
val smsAvailable: Boolean,
val voiceWakeEnabled: Boolean,
val debugBuild: Boolean,
)
enum class InvokeCommandAvailability {
Always,
CameraEnabled,
@@ -17,6 +26,19 @@ enum class InvokeCommandAvailability {
DebugBuild,
}
enum class NodeCapabilityAvailability {
Always,
CameraEnabled,
LocationEnabled,
SmsAvailable,
VoiceWakeEnabled,
}
data class NodeCapabilitySpec(
val name: String,
val availability: NodeCapabilityAvailability = NodeCapabilityAvailability.Always,
)
data class InvokeCommandSpec(
val name: String,
val requiresForeground: Boolean = false,
@@ -24,6 +46,29 @@ data class InvokeCommandSpec(
)
object InvokeCommandRegistry {
val capabilityManifest: List<NodeCapabilitySpec> =
listOf(
NodeCapabilitySpec(name = OpenClawCapability.Canvas.rawValue),
NodeCapabilitySpec(name = OpenClawCapability.Screen.rawValue),
NodeCapabilitySpec(name = OpenClawCapability.Device.rawValue),
NodeCapabilitySpec(
name = OpenClawCapability.Camera.rawValue,
availability = NodeCapabilityAvailability.CameraEnabled,
),
NodeCapabilitySpec(
name = OpenClawCapability.Sms.rawValue,
availability = NodeCapabilityAvailability.SmsAvailable,
),
NodeCapabilitySpec(
name = OpenClawCapability.VoiceWake.rawValue,
availability = NodeCapabilityAvailability.VoiceWakeEnabled,
),
NodeCapabilitySpec(
name = OpenClawCapability.Location.rawValue,
availability = NodeCapabilityAvailability.LocationEnabled,
),
)
val all: List<InvokeCommandSpec> =
listOf(
InvokeCommandSpec(
@@ -118,20 +163,29 @@ object InvokeCommandRegistry {
fun find(command: String): InvokeCommandSpec? = byNameInternal[command]
fun advertisedCommands(
cameraEnabled: Boolean,
locationEnabled: Boolean,
smsAvailable: Boolean,
debugBuild: Boolean,
): List<String> {
fun advertisedCapabilities(flags: NodeRuntimeFlags): List<String> {
return capabilityManifest
.filter { spec ->
when (spec.availability) {
NodeCapabilityAvailability.Always -> true
NodeCapabilityAvailability.CameraEnabled -> flags.cameraEnabled
NodeCapabilityAvailability.LocationEnabled -> flags.locationEnabled
NodeCapabilityAvailability.SmsAvailable -> flags.smsAvailable
NodeCapabilityAvailability.VoiceWakeEnabled -> flags.voiceWakeEnabled
}
}
.map { it.name }
}
fun advertisedCommands(flags: NodeRuntimeFlags): List<String> {
return all
.filter { spec ->
when (spec.availability) {
InvokeCommandAvailability.Always -> true
InvokeCommandAvailability.CameraEnabled -> cameraEnabled
InvokeCommandAvailability.LocationEnabled -> locationEnabled
InvokeCommandAvailability.SmsAvailable -> smsAvailable
InvokeCommandAvailability.DebugBuild -> debugBuild
InvokeCommandAvailability.CameraEnabled -> flags.cameraEnabled
InvokeCommandAvailability.LocationEnabled -> flags.locationEnabled
InvokeCommandAvailability.SmsAvailable -> flags.smsAvailable
InvokeCommandAvailability.DebugBuild -> flags.debugBuild
}
}
.map { it.name }

View File

@@ -1,6 +1,7 @@
package ai.openclaw.android.node
import ai.openclaw.android.protocol.OpenClawCameraCommand
import ai.openclaw.android.protocol.OpenClawCapability
import ai.openclaw.android.protocol.OpenClawDeviceCommand
import ai.openclaw.android.protocol.OpenClawLocationCommand
import ai.openclaw.android.protocol.OpenClawNotificationsCommand
@@ -10,14 +11,61 @@ import org.junit.Assert.assertTrue
import org.junit.Test
class InvokeCommandRegistryTest {
@Test
fun advertisedCapabilities_respectsFeatureAvailability() {
val capabilities =
InvokeCommandRegistry.advertisedCapabilities(
NodeRuntimeFlags(
cameraEnabled = false,
locationEnabled = false,
smsAvailable = false,
voiceWakeEnabled = false,
debugBuild = false,
),
)
assertTrue(capabilities.contains(OpenClawCapability.Canvas.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Screen.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Device.rawValue))
assertFalse(capabilities.contains(OpenClawCapability.Camera.rawValue))
assertFalse(capabilities.contains(OpenClawCapability.Location.rawValue))
assertFalse(capabilities.contains(OpenClawCapability.Sms.rawValue))
assertFalse(capabilities.contains(OpenClawCapability.VoiceWake.rawValue))
}
@Test
fun advertisedCapabilities_includesFeatureCapabilitiesWhenEnabled() {
val capabilities =
InvokeCommandRegistry.advertisedCapabilities(
NodeRuntimeFlags(
cameraEnabled = true,
locationEnabled = true,
smsAvailable = true,
voiceWakeEnabled = true,
debugBuild = false,
),
)
assertTrue(capabilities.contains(OpenClawCapability.Canvas.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Screen.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Device.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Camera.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Location.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.Sms.rawValue))
assertTrue(capabilities.contains(OpenClawCapability.VoiceWake.rawValue))
}
@Test
fun advertisedCommands_respectsFeatureAvailability() {
val commands =
InvokeCommandRegistry.advertisedCommands(
cameraEnabled = false,
locationEnabled = false,
smsAvailable = false,
debugBuild = false,
NodeRuntimeFlags(
cameraEnabled = false,
locationEnabled = false,
smsAvailable = false,
voiceWakeEnabled = false,
debugBuild = false,
),
)
assertFalse(commands.contains(OpenClawCameraCommand.Snap.rawValue))
@@ -40,10 +88,13 @@ class InvokeCommandRegistryTest {
fun advertisedCommands_includesFeatureCommandsWhenEnabled() {
val commands =
InvokeCommandRegistry.advertisedCommands(
cameraEnabled = true,
locationEnabled = true,
smsAvailable = true,
debugBuild = true,
NodeRuntimeFlags(
cameraEnabled = true,
locationEnabled = true,
smsAvailable = true,
voiceWakeEnabled = false,
debugBuild = true,
),
)
assertTrue(commands.contains(OpenClawCameraCommand.Snap.rawValue))