refactor: rename to openclaw
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## Clawdbot Node (Android) (internal)
|
||||
## OpenClaw Node (Android) (internal)
|
||||
|
||||
Modern Android node app: connects to the **Gateway WebSocket** (`_clawdbot-gw._tcp`) and exposes **Canvas + Chat + Camera**.
|
||||
Modern Android node app: connects to the **Gateway WebSocket** (`_openclaw-gw._tcp`) and exposes **Canvas + Chat + Camera**.
|
||||
|
||||
Notes:
|
||||
- The node keeps the connection alive via a **foreground service** (persistent notification with a Disconnect action).
|
||||
@@ -25,7 +25,7 @@ cd apps/android
|
||||
|
||||
1) Start the gateway (on your “master” machine):
|
||||
```bash
|
||||
pnpm clawdbot gateway --port 18789 --verbose
|
||||
pnpm openclaw gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
2) In the Android app:
|
||||
@@ -34,8 +34,8 @@ pnpm clawdbot gateway --port 18789 --verbose
|
||||
|
||||
3) Approve pairing (on the gateway machine):
|
||||
```bash
|
||||
clawdbot nodes pending
|
||||
clawdbot nodes approve <requestId>
|
||||
openclaw nodes pending
|
||||
openclaw nodes approve <requestId>
|
||||
```
|
||||
|
||||
More details: `docs/platforms/android.md`.
|
||||
|
||||
@@ -8,17 +8,17 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "bot.molt.android"
|
||||
namespace = "ai.openclaw.android"
|
||||
compileSdk = 36
|
||||
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
assets.srcDir(file("../../shared/MoltbotKit/Sources/MoltbotKit/Resources"))
|
||||
assets.srcDir(file("../../shared/OpenClawKit/Sources/OpenClawKit/Resources"))
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "bot.molt.android"
|
||||
applicationId = "ai.openclaw.android"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 202601290
|
||||
@@ -65,7 +65,7 @@ androidComponents {
|
||||
val versionName = output.versionName.orNull ?: "0"
|
||||
val buildType = variant.buildType
|
||||
|
||||
val outputFileName = "moltbot-${versionName}-${buildType}.apk"
|
||||
val outputFileName = "openclaw-${versionName}-${buildType}.apk"
|
||||
output.outputFileName = outputFileName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/Theme.MoltbotNode">
|
||||
android:theme="@style/Theme.OpenClawNode">
|
||||
<service
|
||||
android:name=".NodeForegroundService"
|
||||
android:exported="false"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
enum class CameraHudKind {
|
||||
Photo,
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
enum class LocationMode(val rawValue: String) {
|
||||
Off("off"),
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.ApplicationInfo
|
||||
@@ -18,8 +18,8 @@ import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import bot.molt.android.ui.RootScreen
|
||||
import bot.molt.android.ui.MoltbotTheme
|
||||
import ai.openclaw.android.ui.RootScreen
|
||||
import ai.openclaw.android.ui.OpenClawTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@@ -56,7 +56,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
MoltbotTheme {
|
||||
OpenClawTheme {
|
||||
Surface(modifier = Modifier) {
|
||||
RootScreen(viewModel = viewModel)
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import bot.molt.android.gateway.GatewayEndpoint
|
||||
import bot.molt.android.chat.OutgoingAttachment
|
||||
import bot.molt.android.node.CameraCaptureManager
|
||||
import bot.molt.android.node.CanvasController
|
||||
import bot.molt.android.node.ScreenRecordManager
|
||||
import bot.molt.android.node.SmsManager
|
||||
import ai.openclaw.android.gateway.GatewayEndpoint
|
||||
import ai.openclaw.android.chat.OutgoingAttachment
|
||||
import ai.openclaw.android.node.CameraCaptureManager
|
||||
import ai.openclaw.android.node.CanvasController
|
||||
import ai.openclaw.android.node.ScreenRecordManager
|
||||
import ai.openclaw.android.node.SmsManager
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class MainViewModel(app: Application) : AndroidViewModel(app) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.app.Application
|
||||
import android.os.StrictMode
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
@@ -29,7 +29,7 @@ class NodeForegroundService : Service() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ensureChannel()
|
||||
val initial = buildNotification(title = "Moltbot Node", text = "Starting…")
|
||||
val initial = buildNotification(title = "OpenClaw Node", text = "Starting…")
|
||||
startForegroundWithTypes(notification = initial, requiresMic = false)
|
||||
|
||||
val runtime = (application as NodeApp).runtime
|
||||
@@ -44,7 +44,7 @@ class NodeForegroundService : Service() {
|
||||
) { status, server, connected, voiceMode, voiceListening ->
|
||||
Quint(status, server, connected, voiceMode, voiceListening)
|
||||
}.collect { (status, server, connected, voiceMode, voiceListening) ->
|
||||
val title = if (connected) "Moltbot Node · Connected" else "Moltbot Node"
|
||||
val title = if (connected) "OpenClaw Node · Connected" else "OpenClaw Node"
|
||||
val voiceSuffix =
|
||||
if (voiceMode == VoiceWakeMode.Always) {
|
||||
if (voiceListening) " · Voice Wake: Listening" else " · Voice Wake: Paused"
|
||||
@@ -91,7 +91,7 @@ class NodeForegroundService : Service() {
|
||||
"Connection",
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
).apply {
|
||||
description = "Moltbot node connection status"
|
||||
description = "OpenClaw node connection status"
|
||||
setShowBadge(false)
|
||||
}
|
||||
mgr.createNotificationChannel(channel)
|
||||
@@ -163,7 +163,7 @@ class NodeForegroundService : Service() {
|
||||
private const val CHANNEL_ID = "connection"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
|
||||
private const val ACTION_STOP = "bot.molt.android.action.STOP"
|
||||
private const val ACTION_STOP = "ai.openclaw.android.action.STOP"
|
||||
|
||||
fun start(context: Context) {
|
||||
val intent = Intent(context, NodeForegroundService::class.java)
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -7,35 +7,35 @@ import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import androidx.core.content.ContextCompat
|
||||
import bot.molt.android.chat.ChatController
|
||||
import bot.molt.android.chat.ChatMessage
|
||||
import bot.molt.android.chat.ChatPendingToolCall
|
||||
import bot.molt.android.chat.ChatSessionEntry
|
||||
import bot.molt.android.chat.OutgoingAttachment
|
||||
import bot.molt.android.gateway.DeviceAuthStore
|
||||
import bot.molt.android.gateway.DeviceIdentityStore
|
||||
import bot.molt.android.gateway.GatewayClientInfo
|
||||
import bot.molt.android.gateway.GatewayConnectOptions
|
||||
import bot.molt.android.gateway.GatewayDiscovery
|
||||
import bot.molt.android.gateway.GatewayEndpoint
|
||||
import bot.molt.android.gateway.GatewaySession
|
||||
import bot.molt.android.gateway.GatewayTlsParams
|
||||
import bot.molt.android.node.CameraCaptureManager
|
||||
import bot.molt.android.node.LocationCaptureManager
|
||||
import bot.molt.android.BuildConfig
|
||||
import bot.molt.android.node.CanvasController
|
||||
import bot.molt.android.node.ScreenRecordManager
|
||||
import bot.molt.android.node.SmsManager
|
||||
import bot.molt.android.protocol.MoltbotCapability
|
||||
import bot.molt.android.protocol.MoltbotCameraCommand
|
||||
import bot.molt.android.protocol.MoltbotCanvasA2UIAction
|
||||
import bot.molt.android.protocol.MoltbotCanvasA2UICommand
|
||||
import bot.molt.android.protocol.MoltbotCanvasCommand
|
||||
import bot.molt.android.protocol.MoltbotScreenCommand
|
||||
import bot.molt.android.protocol.MoltbotLocationCommand
|
||||
import bot.molt.android.protocol.MoltbotSmsCommand
|
||||
import bot.molt.android.voice.TalkModeManager
|
||||
import bot.molt.android.voice.VoiceWakeManager
|
||||
import ai.openclaw.android.chat.ChatController
|
||||
import ai.openclaw.android.chat.ChatMessage
|
||||
import ai.openclaw.android.chat.ChatPendingToolCall
|
||||
import ai.openclaw.android.chat.ChatSessionEntry
|
||||
import ai.openclaw.android.chat.OutgoingAttachment
|
||||
import ai.openclaw.android.gateway.DeviceAuthStore
|
||||
import ai.openclaw.android.gateway.DeviceIdentityStore
|
||||
import ai.openclaw.android.gateway.GatewayClientInfo
|
||||
import ai.openclaw.android.gateway.GatewayConnectOptions
|
||||
import ai.openclaw.android.gateway.GatewayDiscovery
|
||||
import ai.openclaw.android.gateway.GatewayEndpoint
|
||||
import ai.openclaw.android.gateway.GatewaySession
|
||||
import ai.openclaw.android.gateway.GatewayTlsParams
|
||||
import ai.openclaw.android.node.CameraCaptureManager
|
||||
import ai.openclaw.android.node.LocationCaptureManager
|
||||
import ai.openclaw.android.BuildConfig
|
||||
import ai.openclaw.android.node.CanvasController
|
||||
import ai.openclaw.android.node.ScreenRecordManager
|
||||
import ai.openclaw.android.node.SmsManager
|
||||
import ai.openclaw.android.protocol.OpenClawCapability
|
||||
import ai.openclaw.android.protocol.OpenClawCameraCommand
|
||||
import ai.openclaw.android.protocol.OpenClawCanvasA2UIAction
|
||||
import ai.openclaw.android.protocol.OpenClawCanvasA2UICommand
|
||||
import ai.openclaw.android.protocol.OpenClawCanvasCommand
|
||||
import ai.openclaw.android.protocol.OpenClawScreenCommand
|
||||
import ai.openclaw.android.protocol.OpenClawLocationCommand
|
||||
import ai.openclaw.android.protocol.OpenClawSmsCommand
|
||||
import ai.openclaw.android.voice.TalkModeManager
|
||||
import ai.openclaw.android.voice.VoiceWakeManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -451,38 +451,38 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
private fun buildInvokeCommands(): List<String> =
|
||||
buildList {
|
||||
add(MoltbotCanvasCommand.Present.rawValue)
|
||||
add(MoltbotCanvasCommand.Hide.rawValue)
|
||||
add(MoltbotCanvasCommand.Navigate.rawValue)
|
||||
add(MoltbotCanvasCommand.Eval.rawValue)
|
||||
add(MoltbotCanvasCommand.Snapshot.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.Push.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
add(MoltbotCanvasA2UICommand.Reset.rawValue)
|
||||
add(MoltbotScreenCommand.Record.rawValue)
|
||||
add(OpenClawCanvasCommand.Present.rawValue)
|
||||
add(OpenClawCanvasCommand.Hide.rawValue)
|
||||
add(OpenClawCanvasCommand.Navigate.rawValue)
|
||||
add(OpenClawCanvasCommand.Eval.rawValue)
|
||||
add(OpenClawCanvasCommand.Snapshot.rawValue)
|
||||
add(OpenClawCanvasA2UICommand.Push.rawValue)
|
||||
add(OpenClawCanvasA2UICommand.PushJSONL.rawValue)
|
||||
add(OpenClawCanvasA2UICommand.Reset.rawValue)
|
||||
add(OpenClawScreenCommand.Record.rawValue)
|
||||
if (cameraEnabled.value) {
|
||||
add(MoltbotCameraCommand.Snap.rawValue)
|
||||
add(MoltbotCameraCommand.Clip.rawValue)
|
||||
add(OpenClawCameraCommand.Snap.rawValue)
|
||||
add(OpenClawCameraCommand.Clip.rawValue)
|
||||
}
|
||||
if (locationMode.value != LocationMode.Off) {
|
||||
add(MoltbotLocationCommand.Get.rawValue)
|
||||
add(OpenClawLocationCommand.Get.rawValue)
|
||||
}
|
||||
if (sms.canSendSms()) {
|
||||
add(MoltbotSmsCommand.Send.rawValue)
|
||||
add(OpenClawSmsCommand.Send.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildCapabilities(): List<String> =
|
||||
buildList {
|
||||
add(MoltbotCapability.Canvas.rawValue)
|
||||
add(MoltbotCapability.Screen.rawValue)
|
||||
if (cameraEnabled.value) add(MoltbotCapability.Camera.rawValue)
|
||||
if (sms.canSendSms()) add(MoltbotCapability.Sms.rawValue)
|
||||
add(OpenClawCapability.Canvas.rawValue)
|
||||
add(OpenClawCapability.Screen.rawValue)
|
||||
if (cameraEnabled.value) add(OpenClawCapability.Camera.rawValue)
|
||||
if (sms.canSendSms()) add(OpenClawCapability.Sms.rawValue)
|
||||
if (voiceWakeMode.value != VoiceWakeMode.Off && hasRecordAudioPermission()) {
|
||||
add(MoltbotCapability.VoiceWake.rawValue)
|
||||
add(OpenClawCapability.VoiceWake.rawValue)
|
||||
}
|
||||
if (locationMode.value != LocationMode.Off) {
|
||||
add(MoltbotCapability.Location.rawValue)
|
||||
add(OpenClawCapability.Location.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ class NodeRuntime(context: Context) {
|
||||
val version = resolvedVersionName()
|
||||
val release = Build.VERSION.RELEASE?.trim().orEmpty()
|
||||
val releaseLabel = if (release.isEmpty()) "unknown" else release
|
||||
return "MoltbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
|
||||
return "OpenClawAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
|
||||
}
|
||||
|
||||
private fun buildClientInfo(clientId: String, clientMode: String): GatewayClientInfo {
|
||||
@@ -529,7 +529,7 @@ class NodeRuntime(context: Context) {
|
||||
caps = buildCapabilities(),
|
||||
commands = buildInvokeCommands(),
|
||||
permissions = emptyMap(),
|
||||
client = buildClientInfo(clientId = "moltbot-android", clientMode = "node"),
|
||||
client = buildClientInfo(clientId = "openclaw-android", clientMode = "node"),
|
||||
userAgent = buildUserAgent(),
|
||||
)
|
||||
}
|
||||
@@ -541,7 +541,7 @@ class NodeRuntime(context: Context) {
|
||||
caps = emptyList(),
|
||||
commands = emptyList(),
|
||||
permissions = emptyMap(),
|
||||
client = buildClientInfo(clientId = "moltbot-control-ui", clientMode = "ui"),
|
||||
client = buildClientInfo(clientId = "openclaw-control-ui", clientMode = "ui"),
|
||||
userAgent = buildUserAgent(),
|
||||
)
|
||||
}
|
||||
@@ -665,7 +665,7 @@ class NodeRuntime(context: Context) {
|
||||
val actionId = (userActionObj["id"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty {
|
||||
java.util.UUID.randomUUID().toString()
|
||||
}
|
||||
val name = MoltbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
|
||||
val name = OpenClawCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
|
||||
|
||||
val surfaceId =
|
||||
(userActionObj["surfaceId"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty { "main" }
|
||||
@@ -675,7 +675,7 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
val sessionKey = resolveMainSessionKey()
|
||||
val message =
|
||||
MoltbotCanvasA2UIAction.formatAgentMessage(
|
||||
OpenClawCanvasA2UIAction.formatAgentMessage(
|
||||
actionName = name,
|
||||
sessionKey = sessionKey,
|
||||
surfaceId = surfaceId,
|
||||
@@ -709,7 +709,7 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
try {
|
||||
canvas.eval(
|
||||
MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
OpenClawCanvasA2UIAction.jsDispatchA2UIActionStatus(
|
||||
actionId = actionId,
|
||||
ok = connected && error == null,
|
||||
error = error,
|
||||
@@ -827,10 +827,10 @@ class NodeRuntime(context: Context) {
|
||||
|
||||
private suspend fun handleInvoke(command: String, paramsJson: String?): GatewaySession.InvokeResult {
|
||||
if (
|
||||
command.startsWith(MoltbotCanvasCommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotCanvasA2UICommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotCameraCommand.NamespacePrefix) ||
|
||||
command.startsWith(MoltbotScreenCommand.NamespacePrefix)
|
||||
command.startsWith(OpenClawCanvasCommand.NamespacePrefix) ||
|
||||
command.startsWith(OpenClawCanvasA2UICommand.NamespacePrefix) ||
|
||||
command.startsWith(OpenClawCameraCommand.NamespacePrefix) ||
|
||||
command.startsWith(OpenClawScreenCommand.NamespacePrefix)
|
||||
) {
|
||||
if (!isForeground.value) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -839,13 +839,13 @@ class NodeRuntime(context: Context) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (command.startsWith(MoltbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
|
||||
if (command.startsWith(OpenClawCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
code = "CAMERA_DISABLED",
|
||||
message = "CAMERA_DISABLED: enable Camera in Settings",
|
||||
)
|
||||
}
|
||||
if (command.startsWith(MoltbotLocationCommand.NamespacePrefix) &&
|
||||
if (command.startsWith(OpenClawLocationCommand.NamespacePrefix) &&
|
||||
locationMode.value == LocationMode.Off
|
||||
) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -855,18 +855,18 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
|
||||
return when (command) {
|
||||
MoltbotCanvasCommand.Present.rawValue -> {
|
||||
OpenClawCanvasCommand.Present.rawValue -> {
|
||||
val url = CanvasController.parseNavigateUrl(paramsJson)
|
||||
canvas.navigate(url)
|
||||
GatewaySession.InvokeResult.ok(null)
|
||||
}
|
||||
MoltbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
|
||||
MoltbotCanvasCommand.Navigate.rawValue -> {
|
||||
OpenClawCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
|
||||
OpenClawCanvasCommand.Navigate.rawValue -> {
|
||||
val url = CanvasController.parseNavigateUrl(paramsJson)
|
||||
canvas.navigate(url)
|
||||
GatewaySession.InvokeResult.ok(null)
|
||||
}
|
||||
MoltbotCanvasCommand.Eval.rawValue -> {
|
||||
OpenClawCanvasCommand.Eval.rawValue -> {
|
||||
val js =
|
||||
CanvasController.parseEvalJs(paramsJson)
|
||||
?: return GatewaySession.InvokeResult.error(
|
||||
@@ -884,7 +884,7 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
GatewaySession.InvokeResult.ok("""{"result":${result.toJsonString()}}""")
|
||||
}
|
||||
MoltbotCanvasCommand.Snapshot.rawValue -> {
|
||||
OpenClawCanvasCommand.Snapshot.rawValue -> {
|
||||
val snapshotParams = CanvasController.parseSnapshotParams(paramsJson)
|
||||
val base64 =
|
||||
try {
|
||||
@@ -901,7 +901,7 @@ class NodeRuntime(context: Context) {
|
||||
}
|
||||
GatewaySession.InvokeResult.ok("""{"format":"${snapshotParams.format.rawValue}","base64":"$base64"}""")
|
||||
}
|
||||
MoltbotCanvasA2UICommand.Reset.rawValue -> {
|
||||
OpenClawCanvasA2UICommand.Reset.rawValue -> {
|
||||
val a2uiUrl = resolveA2uiHostUrl()
|
||||
?: return GatewaySession.InvokeResult.error(
|
||||
code = "A2UI_HOST_NOT_CONFIGURED",
|
||||
@@ -917,7 +917,7 @@ class NodeRuntime(context: Context) {
|
||||
val res = canvas.eval(a2uiResetJS)
|
||||
GatewaySession.InvokeResult.ok(res)
|
||||
}
|
||||
MoltbotCanvasA2UICommand.Push.rawValue, MoltbotCanvasA2UICommand.PushJSONL.rawValue -> {
|
||||
OpenClawCanvasA2UICommand.Push.rawValue, OpenClawCanvasA2UICommand.PushJSONL.rawValue -> {
|
||||
val messages =
|
||||
try {
|
||||
decodeA2uiMessages(command, paramsJson)
|
||||
@@ -940,7 +940,7 @@ class NodeRuntime(context: Context) {
|
||||
val res = canvas.eval(js)
|
||||
GatewaySession.InvokeResult.ok(res)
|
||||
}
|
||||
MoltbotCameraCommand.Snap.rawValue -> {
|
||||
OpenClawCameraCommand.Snap.rawValue -> {
|
||||
showCameraHud(message = "Taking photo…", kind = CameraHudKind.Photo)
|
||||
triggerCameraFlash()
|
||||
val res =
|
||||
@@ -954,7 +954,7 @@ class NodeRuntime(context: Context) {
|
||||
showCameraHud(message = "Photo captured", kind = CameraHudKind.Success, autoHideMs = 1600)
|
||||
GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
}
|
||||
MoltbotCameraCommand.Clip.rawValue -> {
|
||||
OpenClawCameraCommand.Clip.rawValue -> {
|
||||
val includeAudio = paramsJson?.contains("\"includeAudio\":true") != false
|
||||
if (includeAudio) externalAudioCaptureActive.value = true
|
||||
try {
|
||||
@@ -973,7 +973,7 @@ class NodeRuntime(context: Context) {
|
||||
if (includeAudio) externalAudioCaptureActive.value = false
|
||||
}
|
||||
}
|
||||
MoltbotLocationCommand.Get.rawValue -> {
|
||||
OpenClawLocationCommand.Get.rawValue -> {
|
||||
val mode = locationMode.value
|
||||
if (!isForeground.value && mode != LocationMode.Always) {
|
||||
return GatewaySession.InvokeResult.error(
|
||||
@@ -1026,7 +1026,7 @@ class NodeRuntime(context: Context) {
|
||||
GatewaySession.InvokeResult.error(code = "LOCATION_UNAVAILABLE", message = message)
|
||||
}
|
||||
}
|
||||
MoltbotScreenCommand.Record.rawValue -> {
|
||||
OpenClawScreenCommand.Record.rawValue -> {
|
||||
// Status pill mirrors screen recording state so it stays visible without overlay stacking.
|
||||
_screenRecordActive.value = true
|
||||
try {
|
||||
@@ -1042,7 +1042,7 @@ class NodeRuntime(context: Context) {
|
||||
_screenRecordActive.value = false
|
||||
}
|
||||
}
|
||||
MoltbotSmsCommand.Send.rawValue -> {
|
||||
OpenClawSmsCommand.Send.rawValue -> {
|
||||
val res = sms.send(paramsJson)
|
||||
if (res.ok) {
|
||||
GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
@@ -1115,7 +1115,7 @@ class NodeRuntime(context: Context) {
|
||||
val raw = if (nodeRaw.isNotBlank()) nodeRaw else operatorRaw
|
||||
if (raw.isBlank()) return null
|
||||
val base = raw.trimEnd('/')
|
||||
return "${base}/__moltbot__/a2ui/?platform=android"
|
||||
return "${base}/__openclaw__/a2ui/?platform=android"
|
||||
}
|
||||
|
||||
private suspend fun ensureA2uiReady(a2uiUrl: String): Boolean {
|
||||
@@ -1150,7 +1150,7 @@ class NodeRuntime(context: Context) {
|
||||
val jsonlField = (obj["jsonl"] as? JsonPrimitive)?.content?.trim().orEmpty()
|
||||
val hasMessagesArray = obj["messages"] is JsonArray
|
||||
|
||||
if (command == MoltbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
|
||||
if (command == OpenClawCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
|
||||
val jsonl = jsonlField
|
||||
if (jsonl.isBlank()) throw IllegalArgumentException("INVALID_REQUEST: jsonl required")
|
||||
val messages =
|
||||
@@ -1207,7 +1207,8 @@ private const val a2uiReadyCheckJS: String =
|
||||
"""
|
||||
(() => {
|
||||
try {
|
||||
return !!globalThis.clawdbotA2UI && typeof globalThis.clawdbotA2UI.applyMessages === 'function';
|
||||
const host = globalThis.openclawA2UI;
|
||||
return !!host && typeof host.applyMessages === 'function';
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
@@ -1218,8 +1219,9 @@ private const val a2uiResetJS: String =
|
||||
"""
|
||||
(() => {
|
||||
try {
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
|
||||
return globalThis.clawdbotA2UI.reset();
|
||||
const host = globalThis.openclawA2UI;
|
||||
if (!host) return { ok: false, error: "missing openclawA2UI" };
|
||||
return host.reset();
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e?.message ?? e) };
|
||||
}
|
||||
@@ -1230,9 +1232,10 @@ private fun a2uiApplyMessagesJS(messagesJson: String): String {
|
||||
return """
|
||||
(() => {
|
||||
try {
|
||||
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
|
||||
const host = globalThis.openclawA2UI;
|
||||
if (!host) return { ok: false, error: "missing openclawA2UI" };
|
||||
const messages = $messagesJson;
|
||||
return globalThis.clawdbotA2UI.applyMessages(messages);
|
||||
return host.applyMessages(messages);
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e?.message ?? e) };
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.Intent
|
||||
@@ -115,7 +115,7 @@ class PermissionRequester(private val activity: ComponentActivity) {
|
||||
|
||||
private fun buildRationaleMessage(permissions: List<String>): String {
|
||||
val labels = permissions.map { permissionLabel(it) }
|
||||
return "Moltbot needs ${labels.joinToString(", ")} permissions to continue."
|
||||
return "OpenClaw needs ${labels.joinToString(", ")} permissions to continue."
|
||||
}
|
||||
|
||||
private fun buildSettingsMessage(permissions: List<String>): String {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
@@ -55,7 +55,7 @@ class ScreenCaptureRequester(private val activity: ComponentActivity) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle("Screen recording required")
|
||||
.setMessage("Moltbot needs to record the screen for this command.")
|
||||
.setMessage("OpenClaw needs to record the screen for this command.")
|
||||
.setPositiveButton("Continue") { _, _ -> cont.resume(true) }
|
||||
.setNegativeButton("Not now") { _, _ -> cont.resume(false) }
|
||||
.setOnCancelListener { cont.resume(false) }
|
||||
@@ -1,8 +1,9 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
@@ -16,11 +17,12 @@ import java.util.UUID
|
||||
|
||||
class SecurePrefs(context: Context) {
|
||||
companion object {
|
||||
val defaultWakeWords: List<String> = listOf("clawd", "claude")
|
||||
val defaultWakeWords: List<String> = listOf("openclaw", "claude")
|
||||
private const val displayNameKey = "node.displayName"
|
||||
private const val voiceWakeModeKey = "voiceWake.mode"
|
||||
}
|
||||
|
||||
private val appContext = context.applicationContext
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
private val masterKey =
|
||||
@@ -28,14 +30,9 @@ class SecurePrefs(context: Context) {
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build()
|
||||
|
||||
private val prefs =
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"moltbot.node.secure",
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
)
|
||||
private val prefs: SharedPreferences by lazy {
|
||||
createPrefs(appContext, "openclaw.node.secure")
|
||||
}
|
||||
|
||||
private val _instanceId = MutableStateFlow(loadOrCreateInstanceId())
|
||||
val instanceId: StateFlow<String> = _instanceId
|
||||
@@ -59,28 +56,24 @@ class SecurePrefs(context: Context) {
|
||||
val preventSleep: StateFlow<Boolean> = _preventSleep
|
||||
|
||||
private val _manualEnabled =
|
||||
MutableStateFlow(readBoolWithMigration("gateway.manual.enabled", "bridge.manual.enabled", false))
|
||||
MutableStateFlow(prefs.getBoolean("gateway.manual.enabled", false))
|
||||
val manualEnabled: StateFlow<Boolean> = _manualEnabled
|
||||
|
||||
private val _manualHost =
|
||||
MutableStateFlow(readStringWithMigration("gateway.manual.host", "bridge.manual.host", ""))
|
||||
MutableStateFlow(prefs.getString("gateway.manual.host", "") ?: "")
|
||||
val manualHost: StateFlow<String> = _manualHost
|
||||
|
||||
private val _manualPort =
|
||||
MutableStateFlow(readIntWithMigration("gateway.manual.port", "bridge.manual.port", 18789))
|
||||
MutableStateFlow(prefs.getInt("gateway.manual.port", 18789))
|
||||
val manualPort: StateFlow<Int> = _manualPort
|
||||
|
||||
private val _manualTls =
|
||||
MutableStateFlow(readBoolWithMigration("gateway.manual.tls", null, true))
|
||||
MutableStateFlow(prefs.getBoolean("gateway.manual.tls", true))
|
||||
val manualTls: StateFlow<Boolean> = _manualTls
|
||||
|
||||
private val _lastDiscoveredStableId =
|
||||
MutableStateFlow(
|
||||
readStringWithMigration(
|
||||
"gateway.lastDiscoveredStableID",
|
||||
"bridge.lastDiscoveredStableId",
|
||||
"",
|
||||
),
|
||||
prefs.getString("gateway.lastDiscoveredStableID", "") ?: "",
|
||||
)
|
||||
val lastDiscoveredStableId: StateFlow<String> = _lastDiscoveredStableId
|
||||
|
||||
@@ -158,9 +151,7 @@ class SecurePrefs(context: Context) {
|
||||
fun loadGatewayToken(): String? {
|
||||
val key = "gateway.token.${_instanceId.value}"
|
||||
val stored = prefs.getString(key, null)?.trim()
|
||||
if (!stored.isNullOrEmpty()) return stored
|
||||
val legacy = prefs.getString("bridge.token.${_instanceId.value}", null)?.trim()
|
||||
return legacy?.takeIf { it.isNotEmpty() }
|
||||
return stored?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun saveGatewayToken(token: String) {
|
||||
@@ -201,6 +192,16 @@ class SecurePrefs(context: Context) {
|
||||
prefs.edit { remove(key) }
|
||||
}
|
||||
|
||||
private fun createPrefs(context: Context, name: String): SharedPreferences {
|
||||
return EncryptedSharedPreferences.create(
|
||||
context,
|
||||
name,
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadOrCreateInstanceId(): String {
|
||||
val existing = prefs.getString("node.instanceId", null)?.trim()
|
||||
if (!existing.isNullOrBlank()) return existing
|
||||
@@ -270,39 +271,4 @@ class SecurePrefs(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun readBoolWithMigration(newKey: String, oldKey: String?, defaultValue: Boolean): Boolean {
|
||||
if (prefs.contains(newKey)) {
|
||||
return prefs.getBoolean(newKey, defaultValue)
|
||||
}
|
||||
if (oldKey != null && prefs.contains(oldKey)) {
|
||||
val value = prefs.getBoolean(oldKey, defaultValue)
|
||||
prefs.edit { putBoolean(newKey, value) }
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
private fun readStringWithMigration(newKey: String, oldKey: String?, defaultValue: String): String {
|
||||
if (prefs.contains(newKey)) {
|
||||
return prefs.getString(newKey, defaultValue) ?: defaultValue
|
||||
}
|
||||
if (oldKey != null && prefs.contains(oldKey)) {
|
||||
val value = prefs.getString(oldKey, defaultValue) ?: defaultValue
|
||||
prefs.edit { putString(newKey, value) }
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
private fun readIntWithMigration(newKey: String, oldKey: String?, defaultValue: Int): Int {
|
||||
if (prefs.contains(newKey)) {
|
||||
return prefs.getInt(newKey, defaultValue)
|
||||
}
|
||||
if (oldKey != null && prefs.contains(oldKey)) {
|
||||
val value = prefs.getInt(oldKey, defaultValue)
|
||||
prefs.edit { putInt(newKey, value) }
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
internal fun normalizeMainKey(raw: String?): String {
|
||||
val trimmed = raw?.trim()
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
enum class VoiceWakeMode(val rawValue: String) {
|
||||
Off("off"),
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
object WakeWords {
|
||||
const val maxWords: Int = 32
|
||||
@@ -1,6 +1,6 @@
|
||||
package bot.molt.android.chat
|
||||
package ai.openclaw.android.chat
|
||||
|
||||
import bot.molt.android.gateway.GatewaySession
|
||||
import ai.openclaw.android.gateway.GatewaySession
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.chat
|
||||
package ai.openclaw.android.chat
|
||||
|
||||
data class ChatMessage(
|
||||
val id: String,
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
object BonjourEscapes {
|
||||
fun decode(input: String): String {
|
||||
@@ -1,6 +1,6 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import bot.molt.android.SecurePrefs
|
||||
import ai.openclaw.android.SecurePrefs
|
||||
|
||||
class DeviceAuthStore(private val prefs: SecurePrefs) {
|
||||
fun loadToken(deviceId: String, role: String): String? {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Base64
|
||||
@@ -21,7 +21,7 @@ data class DeviceIdentity(
|
||||
|
||||
class DeviceIdentityStore(context: Context) {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val identityFile = File(context.filesDir, "moltbot/identity/device.json")
|
||||
private val identityFile = File(context.filesDir, "openclaw/identity/device.json")
|
||||
|
||||
@Synchronized
|
||||
fun loadOrCreate(): DeviceIdentity {
|
||||
@@ -65,9 +65,13 @@ class DeviceIdentityStore(context: Context) {
|
||||
}
|
||||
|
||||
private fun load(): DeviceIdentity? {
|
||||
return readIdentity(identityFile)
|
||||
}
|
||||
|
||||
private fun readIdentity(file: File): DeviceIdentity? {
|
||||
return try {
|
||||
if (!identityFile.exists()) return null
|
||||
val raw = identityFile.readText(Charsets.UTF_8)
|
||||
if (!file.exists()) return null
|
||||
val raw = file.readText(Charsets.UTF_8)
|
||||
val decoded = json.decodeFromString(DeviceIdentity.serializer(), raw)
|
||||
if (decoded.deviceId.isBlank() ||
|
||||
decoded.publicKeyRawBase64.isBlank() ||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
@@ -51,9 +51,9 @@ class GatewayDiscovery(
|
||||
private val nsd = context.getSystemService(NsdManager::class.java)
|
||||
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
|
||||
private val dns = DnsResolver.getInstance()
|
||||
private val serviceType = "_moltbot-gw._tcp."
|
||||
private val wideAreaDomain = "moltbot.internal."
|
||||
private val logTag = "Moltbot/GatewayDiscovery"
|
||||
private val serviceType = "_openclaw-gw._tcp."
|
||||
private val wideAreaDomain = System.getenv("OPENCLAW_WIDE_AREA_DOMAIN")
|
||||
private val logTag = "OpenClaw/GatewayDiscovery"
|
||||
|
||||
private val localById = ConcurrentHashMap<String, GatewayEndpoint>()
|
||||
private val unicastById = ConcurrentHashMap<String, GatewayEndpoint>()
|
||||
@@ -91,7 +91,9 @@ class GatewayDiscovery(
|
||||
|
||||
init {
|
||||
startLocalDiscovery()
|
||||
startUnicastDiscovery(wideAreaDomain)
|
||||
if (!wideAreaDomain.isNullOrBlank()) {
|
||||
startUnicastDiscovery(wideAreaDomain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startLocalDiscovery() {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
data class GatewayEndpoint(
|
||||
val stableId: String,
|
||||
@@ -1,3 +1,3 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
const val GATEWAY_PROTOCOL_VERSION = 3
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import android.util.Log
|
||||
import java.util.Locale
|
||||
@@ -148,7 +148,7 @@ class GatewaySession(
|
||||
try {
|
||||
conn.request("node.event", params, timeoutMs = 8_000)
|
||||
} catch (err: Throwable) {
|
||||
Log.w("MoltbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
|
||||
Log.w("OpenClawGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class GatewaySession(
|
||||
private val connectNonceDeferred = CompletableDeferred<String?>()
|
||||
private val client: OkHttpClient = buildClient()
|
||||
private var socket: WebSocket? = null
|
||||
private val loggerTag = "MoltbotGateway"
|
||||
private val loggerTag = "OpenClawGateway"
|
||||
|
||||
val remoteAddress: String =
|
||||
if (endpoint.host.contains(":")) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import java.security.MessageDigest
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -22,7 +22,7 @@ import androidx.camera.video.VideoRecordEvent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.checkSelfPermission
|
||||
import androidx.core.graphics.scale
|
||||
import bot.molt.android.PermissionRequester
|
||||
import ai.openclaw.android.PermissionRequester
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeout
|
||||
@@ -155,7 +155,7 @@ class CameraCaptureManager(private val context: Context) {
|
||||
provider.unbindAll()
|
||||
provider.bindToLifecycle(owner, selector, videoCapture)
|
||||
|
||||
val file = File.createTempFile("moltbot-clip-", ".mp4")
|
||||
val file = File.createTempFile("openclaw-clip-", ".mp4")
|
||||
val outputOptions = FileOutputOptions.Builder(file).build()
|
||||
|
||||
val finalized = kotlinx.coroutines.CompletableDeferred<VideoRecordEvent.Finalize>()
|
||||
@@ -285,7 +285,7 @@ private suspend fun Context.cameraProvider(): ProcessCameraProvider =
|
||||
/** Returns (jpegBytes, exifOrientation) so caller can rotate the decoded bitmap. */
|
||||
private suspend fun ImageCapture.takeJpegWithExif(executor: Executor): Pair<ByteArray, Int> =
|
||||
suspendCancellableCoroutine { cont ->
|
||||
val file = File.createTempFile("moltbot-snap-", ".jpg")
|
||||
val file = File.createTempFile("openclaw-snap-", ".jpg")
|
||||
val options = ImageCapture.OutputFileOptions.Builder(file).build()
|
||||
takePicture(
|
||||
options,
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
@@ -17,7 +17,7 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import bot.molt.android.BuildConfig
|
||||
import ai.openclaw.android.BuildConfig
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CanvasController {
|
||||
@@ -84,12 +84,12 @@ class CanvasController {
|
||||
withWebViewOnMain { wv ->
|
||||
if (currentUrl == null) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("MoltbotCanvas", "load scaffold: $scaffoldAssetUrl")
|
||||
Log.d("OpenClawCanvas", "load scaffold: $scaffoldAssetUrl")
|
||||
}
|
||||
wv.loadUrl(scaffoldAssetUrl)
|
||||
} else {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("MoltbotCanvas", "load url: $currentUrl")
|
||||
Log.d("OpenClawCanvas", "load url: $currentUrl")
|
||||
}
|
||||
wv.loadUrl(currentUrl)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class CanvasController {
|
||||
val js = """
|
||||
(() => {
|
||||
try {
|
||||
const api = globalThis.__moltbot;
|
||||
const api = globalThis.__openclaw;
|
||||
if (!api) return;
|
||||
if (typeof api.setDebugStatusEnabled === 'function') {
|
||||
api.setDebugStatusEnabled(${if (enabled) "true" else "false"});
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.display.DisplayManager
|
||||
@@ -6,7 +6,7 @@ import android.media.MediaRecorder
|
||||
import android.media.projection.MediaProjectionManager
|
||||
import android.os.Build
|
||||
import android.util.Base64
|
||||
import bot.molt.android.ScreenCaptureRequester
|
||||
import ai.openclaw.android.ScreenCaptureRequester
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -17,13 +17,13 @@ class ScreenRecordManager(private val context: Context) {
|
||||
data class Payload(val payloadJson: String)
|
||||
|
||||
@Volatile private var screenCaptureRequester: ScreenCaptureRequester? = null
|
||||
@Volatile private var permissionRequester: bot.molt.android.PermissionRequester? = null
|
||||
@Volatile private var permissionRequester: ai.openclaw.android.PermissionRequester? = null
|
||||
|
||||
fun attachScreenCaptureRequester(requester: ScreenCaptureRequester) {
|
||||
screenCaptureRequester = requester
|
||||
}
|
||||
|
||||
fun attachPermissionRequester(requester: bot.molt.android.PermissionRequester) {
|
||||
fun attachPermissionRequester(requester: ai.openclaw.android.PermissionRequester) {
|
||||
permissionRequester = requester
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class ScreenRecordManager(private val context: Context) {
|
||||
val height = metrics.heightPixels
|
||||
val densityDpi = metrics.densityDpi
|
||||
|
||||
val file = File.createTempFile("moltbot-screen-", ".mp4")
|
||||
val file = File.createTempFile("openclaw-screen-", ".mp4")
|
||||
if (includeAudio) ensureMicPermission()
|
||||
|
||||
val recorder = createMediaRecorder()
|
||||
@@ -90,7 +90,7 @@ class ScreenRecordManager(private val context: Context) {
|
||||
val surface = recorder.surface
|
||||
virtualDisplay =
|
||||
projection.createVirtualDisplay(
|
||||
"moltbot-screen",
|
||||
"openclaw-screen",
|
||||
width,
|
||||
height,
|
||||
densityDpi,
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.encodeToString
|
||||
import bot.molt.android.PermissionRequester
|
||||
import ai.openclaw.android.PermissionRequester
|
||||
|
||||
/**
|
||||
* Sends SMS messages via the Android SMS API.
|
||||
@@ -1,9 +1,9 @@
|
||||
package bot.molt.android.protocol
|
||||
package ai.openclaw.android.protocol
|
||||
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
object MoltbotCanvasA2UIAction {
|
||||
object OpenClawCanvasA2UIAction {
|
||||
fun extractActionName(userAction: JsonObject): String? {
|
||||
val name =
|
||||
(userAction["name"] as? JsonPrimitive)
|
||||
@@ -61,6 +61,6 @@ object MoltbotCanvasA2UIAction {
|
||||
val err = (error ?: "").replace("\\", "\\\\").replace("\"", "\\\"")
|
||||
val okLiteral = if (ok) "true" else "false"
|
||||
val idEscaped = actionId.replace("\\", "\\\\").replace("\"", "\\\"")
|
||||
return "window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
|
||||
return "window.dispatchEvent(new CustomEvent('openclaw:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package bot.molt.android.protocol
|
||||
package ai.openclaw.android.protocol
|
||||
|
||||
enum class MoltbotCapability(val rawValue: String) {
|
||||
enum class OpenClawCapability(val rawValue: String) {
|
||||
Canvas("canvas"),
|
||||
Camera("camera"),
|
||||
Screen("screen"),
|
||||
@@ -9,7 +9,7 @@ enum class MoltbotCapability(val rawValue: String) {
|
||||
Location("location"),
|
||||
}
|
||||
|
||||
enum class MoltbotCanvasCommand(val rawValue: String) {
|
||||
enum class OpenClawCanvasCommand(val rawValue: String) {
|
||||
Present("canvas.present"),
|
||||
Hide("canvas.hide"),
|
||||
Navigate("canvas.navigate"),
|
||||
@@ -22,7 +22,7 @@ enum class MoltbotCanvasCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class MoltbotCanvasA2UICommand(val rawValue: String) {
|
||||
enum class OpenClawCanvasA2UICommand(val rawValue: String) {
|
||||
Push("canvas.a2ui.push"),
|
||||
PushJSONL("canvas.a2ui.pushJSONL"),
|
||||
Reset("canvas.a2ui.reset"),
|
||||
@@ -33,7 +33,7 @@ enum class MoltbotCanvasA2UICommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class MoltbotCameraCommand(val rawValue: String) {
|
||||
enum class OpenClawCameraCommand(val rawValue: String) {
|
||||
Snap("camera.snap"),
|
||||
Clip("camera.clip"),
|
||||
;
|
||||
@@ -43,7 +43,7 @@ enum class MoltbotCameraCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class MoltbotScreenCommand(val rawValue: String) {
|
||||
enum class OpenClawScreenCommand(val rawValue: String) {
|
||||
Record("screen.record"),
|
||||
;
|
||||
|
||||
@@ -52,7 +52,7 @@ enum class MoltbotScreenCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class MoltbotSmsCommand(val rawValue: String) {
|
||||
enum class OpenClawSmsCommand(val rawValue: String) {
|
||||
Send("sms.send"),
|
||||
;
|
||||
|
||||
@@ -61,7 +61,7 @@ enum class MoltbotSmsCommand(val rawValue: String) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class MoltbotLocationCommand(val rawValue: String) {
|
||||
enum class OpenClawLocationCommand(val rawValue: String) {
|
||||
Get("location.get"),
|
||||
;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.tools
|
||||
package ai.openclaw.android.tools
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -1,8 +1,8 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import bot.molt.android.MainViewModel
|
||||
import bot.molt.android.ui.chat.ChatSheetContent
|
||||
import ai.openclaw.android.MainViewModel
|
||||
import ai.openclaw.android.ui.chat.ChatSheetContent
|
||||
|
||||
@Composable
|
||||
fun ChatSheet(viewModel: MainViewModel) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun MoltbotTheme(content: @Composable () -> Unit) {
|
||||
fun OpenClawTheme(content: @Composable () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val isDark = isSystemInDarkTheme()
|
||||
val colorScheme = if (isDark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.Manifest
|
||||
@@ -65,8 +65,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import androidx.core.content.ContextCompat
|
||||
import bot.molt.android.CameraHudKind
|
||||
import bot.molt.android.MainViewModel
|
||||
import ai.openclaw.android.CameraHudKind
|
||||
import ai.openclaw.android.MainViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -333,7 +333,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
disableForceDarkIfSupported(settings)
|
||||
}
|
||||
if (isDebuggable) {
|
||||
Log.d("MoltbotWebView", "userAgent: ${settings.userAgentString}")
|
||||
Log.d("OpenClawWebView", "userAgent: ${settings.userAgentString}")
|
||||
}
|
||||
isScrollContainer = true
|
||||
overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS
|
||||
@@ -348,7 +348,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
) {
|
||||
if (!isDebuggable) return
|
||||
if (!request.isForMainFrame) return
|
||||
Log.e("MoltbotWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}")
|
||||
Log.e("OpenClawWebView", "onReceivedError: ${error.errorCode} ${error.description} ${request.url}")
|
||||
}
|
||||
|
||||
override fun onReceivedHttpError(
|
||||
@@ -359,14 +359,14 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
if (!isDebuggable) return
|
||||
if (!request.isForMainFrame) return
|
||||
Log.e(
|
||||
"MoltbotWebView",
|
||||
"OpenClawWebView",
|
||||
"onReceivedHttpError: ${errorResponse.statusCode} ${errorResponse.reasonPhrase} ${request.url}",
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String?) {
|
||||
if (isDebuggable) {
|
||||
Log.d("MoltbotWebView", "onPageFinished: $url")
|
||||
Log.d("OpenClawWebView", "onPageFinished: $url")
|
||||
}
|
||||
viewModel.canvas.onPageFinished()
|
||||
}
|
||||
@@ -377,7 +377,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
): Boolean {
|
||||
if (isDebuggable) {
|
||||
Log.e(
|
||||
"MoltbotWebView",
|
||||
"OpenClawWebView",
|
||||
"onRenderProcessGone didCrash=${detail.didCrash()} priorityAtExit=${detail.rendererPriorityAtExit()}",
|
||||
)
|
||||
}
|
||||
@@ -390,7 +390,7 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
if (!isDebuggable) return false
|
||||
val msg = consoleMessage ?: return false
|
||||
Log.d(
|
||||
"MoltbotWebView",
|
||||
"OpenClawWebView",
|
||||
"console ${msg.messageLevel()} @ ${msg.sourceId()}:${msg.lineNumber()} ${msg.message()}",
|
||||
)
|
||||
return false
|
||||
@@ -403,10 +403,6 @@ private fun CanvasView(viewModel: MainViewModel, modifier: Modifier = Modifier)
|
||||
viewModel.handleCanvasA2UIActionFromWebView(payload)
|
||||
}
|
||||
addJavascriptInterface(a2uiBridge, CanvasA2UIActionBridge.interfaceName)
|
||||
addJavascriptInterface(
|
||||
CanvasA2UIActionLegacyBridge(a2uiBridge),
|
||||
CanvasA2UIActionLegacyBridge.interfaceName,
|
||||
)
|
||||
viewModel.canvas.attach(this)
|
||||
}
|
||||
},
|
||||
@@ -428,22 +424,6 @@ private class CanvasA2UIActionBridge(private val onMessage: (String) -> Unit) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val interfaceName: String = "moltbotCanvasA2UIAction"
|
||||
}
|
||||
}
|
||||
|
||||
private class CanvasA2UIActionLegacyBridge(private val bridge: CanvasA2UIActionBridge) {
|
||||
@JavascriptInterface
|
||||
fun canvasAction(payload: String?) {
|
||||
bridge.postMessage(payload)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun postMessage(payload: String?) {
|
||||
bridge.postMessage(payload)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val interfaceName: String = "Android"
|
||||
const val interfaceName: String = "openclawCanvasA2UIAction"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -58,12 +58,12 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import bot.molt.android.BuildConfig
|
||||
import bot.molt.android.LocationMode
|
||||
import bot.molt.android.MainViewModel
|
||||
import bot.molt.android.NodeForegroundService
|
||||
import bot.molt.android.VoiceWakeMode
|
||||
import bot.molt.android.WakeWords
|
||||
import ai.openclaw.android.BuildConfig
|
||||
import ai.openclaw.android.LocationMode
|
||||
import ai.openclaw.android.MainViewModel
|
||||
import ai.openclaw.android.NodeForegroundService
|
||||
import ai.openclaw.android.VoiceWakeMode
|
||||
import ai.openclaw.android.WakeWords
|
||||
|
||||
@Composable
|
||||
fun SettingsSheet(viewModel: MainViewModel) {
|
||||
@@ -457,7 +457,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) {
|
||||
ListItem(
|
||||
headlineContent = { Text("Foreground Only") },
|
||||
supportingContent = { Text("Listens only while Moltbot is open.") },
|
||||
supportingContent = { Text("Listens only while OpenClaw is open.") },
|
||||
trailingContent = {
|
||||
RadioButton(
|
||||
selected = voiceWakeMode == VoiceWakeMode.Foreground,
|
||||
@@ -603,7 +603,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text("While Using") },
|
||||
supportingContent = { Text("Only while Moltbot is open.") },
|
||||
supportingContent = { Text("Only while OpenClaw is open.") },
|
||||
trailingContent = {
|
||||
RadioButton(
|
||||
selected = locationMode == LocationMode.WhileUsing,
|
||||
@@ -650,7 +650,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = { Text("Prevent Sleep") },
|
||||
supportingContent = { Text("Keeps the screen awake while Moltbot is open.") },
|
||||
supportingContent = { Text("Keeps the screen awake while OpenClaw is open.") },
|
||||
trailingContent = { Switch(checked = preventSleep, onCheckedChange = viewModel::setPreventSleep) },
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui
|
||||
package ai.openclaw.android.ui
|
||||
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import bot.molt.android.chat.ChatSessionEntry
|
||||
import ai.openclaw.android.chat.ChatSessionEntry
|
||||
|
||||
@Composable
|
||||
fun ChatComposer(
|
||||
@@ -143,7 +143,7 @@ fun ChatComposer(
|
||||
value = input,
|
||||
onValueChange = { input = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = { Text("Message Clawd…") },
|
||||
placeholder = { Text("Message OpenClaw…") },
|
||||
minLines = 2,
|
||||
maxLines = 6,
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Base64
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -20,8 +20,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.unit.dp
|
||||
import bot.molt.android.chat.ChatMessage
|
||||
import bot.molt.android.chat.ChatPendingToolCall
|
||||
import ai.openclaw.android.chat.ChatMessage
|
||||
import ai.openclaw.android.chat.ChatPendingToolCall
|
||||
|
||||
@Composable
|
||||
fun ChatMessageListCard(
|
||||
@@ -103,7 +103,7 @@ private fun EmptyChatHint(modifier: Modifier = Modifier) {
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Text(
|
||||
text = "Message Clawd…",
|
||||
text = "Message OpenClaw…",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Base64
|
||||
@@ -31,10 +31,10 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.foundation.Image
|
||||
import bot.molt.android.chat.ChatMessage
|
||||
import bot.molt.android.chat.ChatMessageContent
|
||||
import bot.molt.android.chat.ChatPendingToolCall
|
||||
import bot.molt.android.tools.ToolDisplayRegistry
|
||||
import ai.openclaw.android.chat.ChatMessage
|
||||
import ai.openclaw.android.chat.ChatMessageContent
|
||||
import ai.openclaw.android.chat.ChatPendingToolCall
|
||||
import ai.openclaw.android.tools.ToolDisplayRegistry
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import bot.molt.android.chat.ChatSessionEntry
|
||||
import ai.openclaw.android.chat.ChatSessionEntry
|
||||
|
||||
@Composable
|
||||
fun ChatSessionsDialog(
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
@@ -19,8 +19,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import bot.molt.android.MainViewModel
|
||||
import bot.molt.android.chat.OutgoingAttachment
|
||||
import ai.openclaw.android.MainViewModel
|
||||
import ai.openclaw.android.chat.OutgoingAttachment
|
||||
import java.io.ByteArrayOutputStream
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -1,6 +1,6 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import bot.molt.android.chat.ChatSessionEntry
|
||||
import ai.openclaw.android.chat.ChatSessionEntry
|
||||
|
||||
private const val RECENT_WINDOW_MS = 24 * 60 * 60 * 1000L
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import android.media.MediaDataSource
|
||||
import kotlin.math.min
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
@@ -20,9 +20,9 @@ import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.UtteranceProgressListener
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import bot.molt.android.gateway.GatewaySession
|
||||
import bot.molt.android.isCanonicalMainSessionKey
|
||||
import bot.molt.android.normalizeMainKey
|
||||
import ai.openclaw.android.gateway.GatewaySession
|
||||
import ai.openclaw.android.isCanonicalMainSessionKey
|
||||
import ai.openclaw.android.normalizeMainKey
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.UUID
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
object VoiceWakeCommandExtractor {
|
||||
fun extractCommand(text: String, triggerWords: List<String>): String? {
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -1,3 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Moltbot Node</string>
|
||||
<string name="app_name">OpenClaw Node</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<resources>
|
||||
<style name="Theme.MoltbotNode" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Theme.OpenClawNode" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<base-config cleartextTrafficPermitted="true" tools:ignore="InsecureBaseConfiguration" />
|
||||
<!-- Allow HTTP for tailnet/local dev endpoints (e.g. canvas/background web). -->
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">moltbot.internal</domain>
|
||||
<domain includeSubdomains="true">openclaw.local</domain>
|
||||
</domain-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">ts.net</domain>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Intent
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android
|
||||
package ai.openclaw.android
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
@@ -7,12 +7,12 @@ import org.junit.Test
|
||||
class WakeWordsTest {
|
||||
@Test
|
||||
fun parseCommaSeparatedTrimsAndDropsEmpty() {
|
||||
assertEquals(listOf("clawd", "claude"), WakeWords.parseCommaSeparated(" clawd , claude, , "))
|
||||
assertEquals(listOf("openclaw", "claude"), WakeWords.parseCommaSeparated(" openclaw , claude, , "))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sanitizeTrimsCapsAndFallsBack() {
|
||||
val defaults = listOf("clawd", "claude")
|
||||
val defaults = listOf("openclaw", "claude")
|
||||
val long = "x".repeat(WakeWords.maxWordLength + 10)
|
||||
val words = listOf(" ", " hello ", long)
|
||||
|
||||
@@ -26,7 +26,7 @@ class WakeWordsTest {
|
||||
|
||||
@Test
|
||||
fun sanitizeLimitsWordCount() {
|
||||
val defaults = listOf("clawd")
|
||||
val defaults = listOf("openclaw")
|
||||
val words = (1..(WakeWords.maxWords + 5)).map { "w$it" }
|
||||
val sanitized = WakeWords.sanitize(words, defaults)
|
||||
assertEquals(WakeWords.maxWords, sanitized.size)
|
||||
@@ -36,15 +36,15 @@ class WakeWordsTest {
|
||||
|
||||
@Test
|
||||
fun parseIfChangedSkipsWhenUnchanged() {
|
||||
val current = listOf("clawd", "claude")
|
||||
val parsed = WakeWords.parseIfChanged(" clawd , claude ", current)
|
||||
val current = listOf("openclaw", "claude")
|
||||
val parsed = WakeWords.parseIfChanged(" openclaw , claude ", current)
|
||||
assertNull(parsed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseIfChangedReturnsUpdatedList() {
|
||||
val current = listOf("clawd")
|
||||
val parsed = WakeWords.parseIfChanged(" clawd , jarvis ", current)
|
||||
assertEquals(listOf("clawd", "jarvis"), parsed)
|
||||
val current = listOf("openclaw")
|
||||
val parsed = WakeWords.parseIfChanged(" openclaw , jarvis ", current)
|
||||
assertEquals(listOf("openclaw", "jarvis"), parsed)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.gateway
|
||||
package ai.openclaw.android.gateway
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
@@ -12,7 +12,7 @@ class BonjourEscapesTest {
|
||||
|
||||
@Test
|
||||
fun decodeDecodesDecimalEscapes() {
|
||||
assertEquals("Moltbot Gateway", BonjourEscapes.decode("Moltbot\\032Gateway"))
|
||||
assertEquals("OpenClaw Gateway", BonjourEscapes.decode("OpenClaw\\032Gateway"))
|
||||
assertEquals("A B", BonjourEscapes.decode("A\\032B"))
|
||||
assertEquals("Peter\u2019s Mac", BonjourEscapes.decode("Peter\\226\\128\\153s Mac"))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.node
|
||||
package ai.openclaw.android.node
|
||||
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
@@ -1,28 +1,28 @@
|
||||
package bot.molt.android.protocol
|
||||
package ai.openclaw.android.protocol
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class MoltbotCanvasA2UIActionTest {
|
||||
class OpenClawCanvasA2UIActionTest {
|
||||
@Test
|
||||
fun extractActionNameAcceptsNameOrAction() {
|
||||
val nameObj = Json.parseToJsonElement("{\"name\":\"Hello\"}").jsonObject
|
||||
assertEquals("Hello", MoltbotCanvasA2UIAction.extractActionName(nameObj))
|
||||
assertEquals("Hello", OpenClawCanvasA2UIAction.extractActionName(nameObj))
|
||||
|
||||
val actionObj = Json.parseToJsonElement("{\"action\":\"Wave\"}").jsonObject
|
||||
assertEquals("Wave", MoltbotCanvasA2UIAction.extractActionName(actionObj))
|
||||
assertEquals("Wave", OpenClawCanvasA2UIAction.extractActionName(actionObj))
|
||||
|
||||
val fallbackObj =
|
||||
Json.parseToJsonElement("{\"name\":\" \",\"action\":\"Fallback\"}").jsonObject
|
||||
assertEquals("Fallback", MoltbotCanvasA2UIAction.extractActionName(fallbackObj))
|
||||
assertEquals("Fallback", OpenClawCanvasA2UIAction.extractActionName(fallbackObj))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formatAgentMessageMatchesSharedSpec() {
|
||||
val msg =
|
||||
MoltbotCanvasA2UIAction.formatAgentMessage(
|
||||
OpenClawCanvasA2UIAction.formatAgentMessage(
|
||||
actionName = "Get Weather",
|
||||
sessionKey = "main",
|
||||
surfaceId = "main",
|
||||
@@ -40,9 +40,9 @@ class MoltbotCanvasA2UIActionTest {
|
||||
|
||||
@Test
|
||||
fun jsDispatchA2uiStatusIsStable() {
|
||||
val js = MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId = "a1", ok = true, error = null)
|
||||
val js = OpenClawCanvasA2UIAction.jsDispatchA2UIActionStatus(actionId = "a1", ok = true, error = null)
|
||||
assertEquals(
|
||||
"window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"a1\", ok: true, error: \"\" } }));",
|
||||
"window.dispatchEvent(new CustomEvent('openclaw:a2ui-action-status', { detail: { id: \"a1\", ok: true, error: \"\" } }));",
|
||||
js,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ai.openclaw.android.protocol
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class OpenClawProtocolConstantsTest {
|
||||
@Test
|
||||
fun canvasCommandsUseStableStrings() {
|
||||
assertEquals("canvas.present", OpenClawCanvasCommand.Present.rawValue)
|
||||
assertEquals("canvas.hide", OpenClawCanvasCommand.Hide.rawValue)
|
||||
assertEquals("canvas.navigate", OpenClawCanvasCommand.Navigate.rawValue)
|
||||
assertEquals("canvas.eval", OpenClawCanvasCommand.Eval.rawValue)
|
||||
assertEquals("canvas.snapshot", OpenClawCanvasCommand.Snapshot.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun a2uiCommandsUseStableStrings() {
|
||||
assertEquals("canvas.a2ui.push", OpenClawCanvasA2UICommand.Push.rawValue)
|
||||
assertEquals("canvas.a2ui.pushJSONL", OpenClawCanvasA2UICommand.PushJSONL.rawValue)
|
||||
assertEquals("canvas.a2ui.reset", OpenClawCanvasA2UICommand.Reset.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun capabilitiesUseStableStrings() {
|
||||
assertEquals("canvas", OpenClawCapability.Canvas.rawValue)
|
||||
assertEquals("camera", OpenClawCapability.Camera.rawValue)
|
||||
assertEquals("screen", OpenClawCapability.Screen.rawValue)
|
||||
assertEquals("voiceWake", OpenClawCapability.VoiceWake.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun screenCommandsUseStableStrings() {
|
||||
assertEquals("screen.record", OpenClawScreenCommand.Record.rawValue)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package bot.molt.android.ui.chat
|
||||
package ai.openclaw.android.ui.chat
|
||||
|
||||
import bot.molt.android.chat.ChatSessionEntry
|
||||
import ai.openclaw.android.chat.ChatSessionEntry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
@@ -1,4 +1,4 @@
|
||||
package bot.molt.android.voice
|
||||
package ai.openclaw.android.voice
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
@@ -7,13 +7,13 @@ import org.junit.Test
|
||||
class VoiceWakeCommandExtractorTest {
|
||||
@Test
|
||||
fun extractsCommandAfterTriggerWord() {
|
||||
val res = VoiceWakeCommandExtractor.extractCommand("Claude take a photo", listOf("clawd", "claude"))
|
||||
val res = VoiceWakeCommandExtractor.extractCommand("Claude take a photo", listOf("openclaw", "claude"))
|
||||
assertEquals("take a photo", res)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extractsCommandWithPunctuation() {
|
||||
val res = VoiceWakeCommandExtractor.extractCommand("hey clawd, what's the weather?", listOf("clawd"))
|
||||
val res = VoiceWakeCommandExtractor.extractCommand("hey openclaw, what's the weather?", listOf("openclaw"))
|
||||
assertEquals("what's the weather?", res)
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package bot.molt.android.protocol
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class MoltbotProtocolConstantsTest {
|
||||
@Test
|
||||
fun canvasCommandsUseStableStrings() {
|
||||
assertEquals("canvas.present", MoltbotCanvasCommand.Present.rawValue)
|
||||
assertEquals("canvas.hide", MoltbotCanvasCommand.Hide.rawValue)
|
||||
assertEquals("canvas.navigate", MoltbotCanvasCommand.Navigate.rawValue)
|
||||
assertEquals("canvas.eval", MoltbotCanvasCommand.Eval.rawValue)
|
||||
assertEquals("canvas.snapshot", MoltbotCanvasCommand.Snapshot.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun a2uiCommandsUseStableStrings() {
|
||||
assertEquals("canvas.a2ui.push", MoltbotCanvasA2UICommand.Push.rawValue)
|
||||
assertEquals("canvas.a2ui.pushJSONL", MoltbotCanvasA2UICommand.PushJSONL.rawValue)
|
||||
assertEquals("canvas.a2ui.reset", MoltbotCanvasA2UICommand.Reset.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun capabilitiesUseStableStrings() {
|
||||
assertEquals("canvas", MoltbotCapability.Canvas.rawValue)
|
||||
assertEquals("camera", MoltbotCapability.Camera.rawValue)
|
||||
assertEquals("screen", MoltbotCapability.Screen.rawValue)
|
||||
assertEquals("voiceWake", MoltbotCapability.VoiceWake.rawValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun screenCommandsUseStableStrings() {
|
||||
assertEquals("screen.record", MoltbotScreenCommand.Record.rawValue)
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,5 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "MoltbotNodeAndroid"
|
||||
rootProject.name = "OpenClawNodeAndroid"
|
||||
include(":app")
|
||||
|
||||
Reference in New Issue
Block a user