Skip to content

Commit 85beee6

Browse files
authored
docs: clarify inline code comments
Comment-only follow-up documenting reusable gateway, auth, proxy, device, Talk, session, and agent helper contracts.\n\nVerification: git diff --check plus targeted tests recorded in PR body.
1 parent 75e0053 commit 85beee6

262 files changed

Lines changed: 2351 additions & 61 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/android/app/src/main/java/ai/openclaw/app/AssistantLaunch.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ package ai.openclaw.app
22

33
import android.content.Intent
44

5+
/** Android Assistant entry point used by manifest-declared app actions. */
56
const val actionAskOpenClaw = "ai.openclaw.app.action.ASK_OPENCLAW"
7+
8+
/** Debug action that opens the Voice tab directly for Android E2E automation. */
69
const val actionOpenVoiceE2e = "ai.openclaw.app.debug.OPEN_VOICE_E2E"
10+
11+
/** Intent extra that carries an optional assistant prompt for app actions. */
712
const val extraAssistantPrompt = "prompt"
813

14+
/**
15+
* Top-level home destinations that external actions may request.
16+
*/
917
enum class HomeDestination {
1018
Connect,
1119
Chat,
@@ -14,20 +22,30 @@ enum class HomeDestination {
1422
Settings,
1523
}
1624

25+
/**
26+
* Normalized launch request from Android Assistant or explicit app actions.
27+
*/
1728
data class AssistantLaunchRequest(
1829
val source: String,
1930
val prompt: String?,
2031
val autoSend: Boolean,
2132
)
2233

34+
/**
35+
* Parses app-owned navigation actions that should open a specific home tab.
36+
*/
2337
fun parseHomeDestinationIntent(intent: Intent?): HomeDestination? {
2438
val action = intent?.action ?: return null
2539
return when {
40+
// Debug-only shortcut keeps E2E navigation out of release builds.
2641
BuildConfig.DEBUG && action == actionOpenVoiceE2e -> HomeDestination.Voice
2742
else -> null
2843
}
2944
}
3045

46+
/**
47+
* Parse external assistant entry points without starting any UI side effects.
48+
*/
3149
fun parseAssistantLaunchIntent(intent: Intent?): AssistantLaunchRequest? {
3250
val action = intent?.action ?: return null
3351
return when (action) {

apps/android/app/src/main/java/ai/openclaw/app/CameraHudState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package ai.openclaw.app
22

3+
/** Camera HUD state categories shown over the Android UI during capture. */
34
enum class CameraHudKind {
45
Photo,
56
Recording,
67
Success,
78
Error,
89
}
910

11+
/** One-shot camera HUD message keyed by token so repeated text still replays. */
1012
data class CameraHudState(
1113
val token: Long,
1214
val kind: CameraHudKind,

apps/android/app/src/main/java/ai/openclaw/app/DeviceNames.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.os.Build
55
import android.provider.Settings
66

77
object DeviceNames {
8+
/** Prefers the user-visible Android device name, then falls back to manufacturer/model text. */
89
fun bestDefaultNodeName(context: Context): String {
910
val deviceName =
1011
runCatching {
@@ -15,6 +16,8 @@ object DeviceNames {
1516

1617
if (deviceName.isNotEmpty()) return deviceName
1718

19+
// Manufacturer/model are best-effort platform fields; keep the final
20+
// fallback stable so stored default names do not become blank.
1821
val model =
1922
listOfNotNull(Build.MANUFACTURER?.takeIf { it.isNotBlank() }, Build.MODEL?.takeIf { it.isNotBlank() })
2023
.joinToString(" ")

apps/android/app/src/main/java/ai/openclaw/app/LocationMode.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package ai.openclaw.app
22

3+
/**
4+
* Persisted location capture mode advertised to the gateway.
5+
*/
36
enum class LocationMode(
47
val rawValue: String,
58
) {
@@ -8,8 +11,10 @@ enum class LocationMode(
811
;
912

1013
companion object {
14+
/** Parses persisted location mode text while migrating old always-on configs to while-using. */
1115
fun fromRawValue(raw: String?): LocationMode {
1216
val normalized = raw?.trim()?.lowercase()
17+
// Older configs used "always"; Android node currently exposes while-using location only.
1318
if (normalized == "always") return WhileUsing
1419
return entries.firstOrNull { it.rawValue.lowercase() == normalized } ?: Off
1520
}

apps/android/app/src/main/java/ai/openclaw/app/MainActivity.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import androidx.lifecycle.lifecycleScope
1515
import androidx.lifecycle.repeatOnLifecycle
1616
import kotlinx.coroutines.launch
1717

18+
/**
19+
* Main Android activity that owns Compose UI attachment and runtime UI wiring.
20+
*/
1821
class MainActivity : ComponentActivity() {
1922
private val viewModel: MainViewModel by viewModels()
2023
private lateinit var permissionRequester: PermissionRequester
@@ -43,6 +46,7 @@ class MainActivity : ComponentActivity() {
4346
repeatOnLifecycle(Lifecycle.State.STARTED) {
4447
viewModel.runtimeInitialized.collect { ready ->
4548
if (!ready || didAttachRuntimeUi) return@collect
49+
// Runtime UI helpers need an Activity owner, so attach once after NodeRuntime is ready.
4650
viewModel.attachRuntimeUi(owner = this@MainActivity, permissionRequester = permissionRequester)
4751
didAttachRuntimeUi = true
4852
if (!didStartNodeService) {
@@ -78,6 +82,9 @@ class MainActivity : ComponentActivity() {
7882
handleAssistantIntent(intent)
7983
}
8084

85+
/**
86+
* Routes assistant/app-action intents into ViewModel state without recreating the activity.
87+
*/
8188
private fun handleAssistantIntent(intent: android.content.Intent?) {
8289
parseHomeDestinationIntent(intent)?.let { destination ->
8390
viewModel.requestHomeDestination(destination)

apps/android/app/src/main/java/ai/openclaw/app/MainViewModel.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import kotlinx.coroutines.flow.flatMapLatest
2222
import kotlinx.coroutines.flow.flowOf
2323
import kotlinx.coroutines.flow.stateIn
2424

25+
/**
26+
* UI-facing bridge that exposes NodeRuntime and preference state as Compose-friendly StateFlows.
27+
*/
2528
@OptIn(ExperimentalCoroutinesApi::class)
2629
class MainViewModel(
2730
app: Application,
@@ -39,6 +42,9 @@ class MainViewModel(
3942
private val _pendingAssistantAutoSend = MutableStateFlow<String?>(null)
4043
val pendingAssistantAutoSend: StateFlow<String?> = _pendingAssistantAutoSend
4144

45+
/**
46+
* Lazily starts NodeRuntime and preserves the current foreground bit across startup.
47+
*/
4248
private fun ensureRuntime(): NodeRuntime {
4349
runtimeRef.value?.let { return it }
4450
val runtime = nodeApp.ensureRuntime()
@@ -47,6 +53,9 @@ class MainViewModel(
4753
return runtime
4854
}
4955

56+
/**
57+
* Adapts a runtime StateFlow to a stable ViewModel StateFlow before runtime startup.
58+
*/
5059
private fun <T> runtimeState(
5160
initial: T,
5261
selector: (NodeRuntime) -> StateFlow<T>,
@@ -185,6 +194,9 @@ class MainViewModel(
185194
val sms: SmsManager
186195
get() = ensureRuntime().sms
187196

197+
/**
198+
* Attaches Activity-owned permission and lifecycle seams after runtime initialization.
199+
*/
188200
fun attachRuntimeUi(
189201
owner: LifecycleOwner,
190202
permissionRequester: PermissionRequester,
@@ -195,6 +207,9 @@ class MainViewModel(
195207
runtime.sms.attachPermissionRequester(permissionRequester)
196208
}
197209

210+
/**
211+
* Starts runtime on foreground entry only after onboarding has completed.
212+
*/
198213
fun setForeground(value: Boolean) {
199214
foreground = value
200215
val runtime =
@@ -254,24 +269,28 @@ class MainViewModel(
254269
prefs.setGatewayPassword(value)
255270
}
256271

272+
/** Clears setup credentials through the runtime so active gateway sessions drop stale auth state. */
257273
fun resetGatewaySetupAuth() {
258274
ensureRuntime().resetGatewaySetupAuth()
259275
}
260276

277+
/** Marks onboarding complete and starts the runtime before UI observes connected-state flows. */
261278
fun setOnboardingCompleted(value: Boolean) {
262279
if (value) {
263280
ensureRuntime()
264281
}
265282
prefs.setOnboardingCompleted(value)
266283
}
267284

285+
/** Re-enters gateway setup after disconnecting and clearing one-time setup credentials. */
268286
fun pairNewGateway() {
269287
runtimeRef.value?.disconnect()
270288
resetGatewaySetupAuth()
271289
_startOnboardingAtGatewaySetup.value = true
272290
prefs.setOnboardingCompleted(false)
273291
}
274292

293+
/** Acknowledges the one-shot request that opens onboarding at the gateway setup step. */
275294
fun clearGatewaySetupStartRequest() {
276295
_startOnboardingAtGatewaySetup.value = false
277296
}
@@ -315,6 +334,7 @@ class MainViewModel(
315334
ensureRuntime().setVoiceScreenActive(active)
316335
}
317336

337+
/** Routes assistant intents into chat, either as a draft or queued auto-send prompt. */
318338
fun handleAssistantLaunch(request: AssistantLaunchRequest) {
319339
_requestedHomeDestination.value = HomeDestination.Chat
320340
if (request.autoSend) {

apps/android/app/src/main/java/ai/openclaw/app/NodeApp.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,27 @@ package ai.openclaw.app
33
import android.app.Application
44
import android.os.StrictMode
55

6+
/**
7+
* Android Application singleton that owns process-wide secure prefs and lazy NodeRuntime startup.
8+
*/
69
class NodeApp : Application() {
710
val prefs: SecurePrefs by lazy { SecurePrefs(this) }
811

912
@Volatile private var runtimeInstance: NodeRuntime? = null
1013

14+
/**
15+
* Returns the single NodeRuntime for this process, creating it on first use.
16+
*/
1117
fun ensureRuntime(): NodeRuntime {
1218
runtimeInstance?.let { return it }
1319
return synchronized(this) {
1420
runtimeInstance ?: NodeRuntime(this, prefs).also { runtimeInstance = it }
1521
}
1622
}
1723

24+
/**
25+
* Reads the runtime without forcing startup, used by lifecycle probes and services.
26+
*/
1827
fun peekRuntime(): NodeRuntime? = runtimeInstance
1928

2029
override fun onCreate() {

apps/android/app/src/main/java/ai/openclaw/app/NodeForegroundService.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import kotlinx.coroutines.cancel
1919
import kotlinx.coroutines.flow.combine
2020
import kotlinx.coroutines.launch
2121

22+
/** Foreground service that keeps the Android node connection and voice capture visible to the OS. */
2223
class NodeForegroundService : Service() {
2324
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
2425
private var notificationJob: Job? = null
@@ -36,6 +37,8 @@ class NodeForegroundService : Service() {
3637
stopSelf()
3738
return
3839
}
40+
// Split connection and capture flows before combining so notification text
41+
// can update without restarting runtime-owned connection work.
3942
notificationJob =
4043
scope.launch {
4144
combine(
@@ -181,6 +184,7 @@ class NodeForegroundService : Service() {
181184
private fun startForegroundWithTypes(notification: Notification) {
182185
val serviceTypes = foregroundServiceTypesForVoiceMode(voiceCaptureMode)
183186
if (didStartForeground) {
187+
// Re-issue startForeground when Talk mode toggles so Android sees the microphone service type.
184188
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, serviceTypes)
185189
return
186190
}
@@ -196,16 +200,19 @@ class NodeForegroundService : Service() {
196200
private const val ACTION_SET_VOICE_CAPTURE_MODE = "ai.openclaw.app.action.SET_VOICE_CAPTURE_MODE"
197201
private const val EXTRA_VOICE_CAPTURE_MODE = "ai.openclaw.app.extra.VOICE_CAPTURE_MODE"
198202

203+
/** Starts the persistent node foreground service from UI lifecycle code. */
199204
fun start(context: Context) {
200205
val intent = Intent(context, NodeForegroundService::class.java)
201206
context.startForegroundService(intent)
202207
}
203208

209+
/** Requests disconnect through the service action path so notification actions and UI share behavior. */
204210
fun stop(context: Context) {
205211
val intent = Intent(context, NodeForegroundService::class.java).setAction(ACTION_STOP)
206212
context.startService(intent)
207213
}
208214

215+
/** Updates Android's foreground-service type before voice capture mode changes require microphone access. */
209216
fun setVoiceCaptureMode(
210217
context: Context,
211218
mode: VoiceCaptureMode,
@@ -215,6 +222,7 @@ class NodeForegroundService : Service() {
215222
.setAction(ACTION_SET_VOICE_CAPTURE_MODE)
216223
.putExtra(EXTRA_VOICE_CAPTURE_MODE, mode.name)
217224
if (mode == VoiceCaptureMode.TalkMode) {
225+
// Microphone foreground service type must be declared before Talk capture starts.
218226
ContextCompat.startForegroundService(context, intent)
219227
} else {
220228
context.startService(intent)
@@ -223,6 +231,9 @@ class NodeForegroundService : Service() {
223231
}
224232
}
225233

234+
/**
235+
* Foreground-service type mask required by Android for the current voice capture mode.
236+
*/
226237
internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
227238
val base = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
228239
return if (mode == VoiceCaptureMode.TalkMode) {
@@ -232,6 +243,9 @@ internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
232243
}
233244
}
234245

246+
/**
247+
* Compact notification suffix for voice state; kept pure for service-notification tests.
248+
*/
235249
internal fun voiceNotificationSuffix(
236250
mode: VoiceCaptureMode,
237251
manualMicEnabled: Boolean,
@@ -260,20 +274,23 @@ private fun String?.toVoiceCaptureMode(): VoiceCaptureMode =
260274
it.name == this
261275
} ?: VoiceCaptureMode.Off
262276

277+
/** Connection fields that drive foreground notification title/body text. */
263278
private data class VoiceNotificationBase(
264279
val status: String,
265280
val server: String?,
266281
val connected: Boolean,
267282
val mode: VoiceCaptureMode,
268283
)
269284

285+
/** Voice capture fields that affect foreground-service type and suffix. */
270286
private data class VoiceNotificationCapture(
271287
val micEnabled: Boolean,
272288
val micListening: Boolean,
273289
val talkListening: Boolean,
274290
val talkSpeaking: Boolean,
275291
)
276292

293+
/** Aggregated notification state from runtime flows. */
277294
private data class VoiceNotificationState(
278295
val base: VoiceNotificationBase,
279296
val capture: VoiceNotificationCapture,

0 commit comments

Comments
 (0)