Mobile: prevent sleep setting

This commit is contained in:
Peter Steinberger
2025-12-17 21:01:47 +01:00
parent cc1d8060c4
commit 7fe7c30b17
7 changed files with 67 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ package com.steipete.clawdis.node
import android.Manifest import android.Manifest
import android.os.Bundle import android.os.Bundle
import android.os.Build import android.os.Build
import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
@@ -10,7 +11,11 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.steipete.clawdis.node.ui.RootScreen import com.steipete.clawdis.node.ui.RootScreen
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val viewModel: MainViewModel by viewModels() private val viewModel: MainViewModel by viewModels()
@@ -21,6 +26,19 @@ class MainActivity : ComponentActivity() {
requestNotificationPermissionIfNeeded() requestNotificationPermissionIfNeeded()
NodeForegroundService.start(this) NodeForegroundService.start(this)
viewModel.camera.attachLifecycleOwner(this) viewModel.camera.attachLifecycleOwner(this)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.preventSleep.collect { enabled ->
if (enabled) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
}
}
setContent { setContent {
MaterialTheme { MaterialTheme {
Surface(modifier = Modifier) { Surface(modifier = Modifier) {

View File

@@ -23,6 +23,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
val instanceId: StateFlow<String> = runtime.instanceId val instanceId: StateFlow<String> = runtime.instanceId
val displayName: StateFlow<String> = runtime.displayName val displayName: StateFlow<String> = runtime.displayName
val cameraEnabled: StateFlow<Boolean> = runtime.cameraEnabled val cameraEnabled: StateFlow<Boolean> = runtime.cameraEnabled
val preventSleep: StateFlow<Boolean> = runtime.preventSleep
val wakeWords: StateFlow<List<String>> = runtime.wakeWords val wakeWords: StateFlow<List<String>> = runtime.wakeWords
val manualEnabled: StateFlow<Boolean> = runtime.manualEnabled val manualEnabled: StateFlow<Boolean> = runtime.manualEnabled
val manualHost: StateFlow<String> = runtime.manualHost val manualHost: StateFlow<String> = runtime.manualHost
@@ -44,6 +45,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
runtime.setCameraEnabled(value) runtime.setCameraEnabled(value)
} }
fun setPreventSleep(value: Boolean) {
runtime.setPreventSleep(value)
}
fun setManualEnabled(value: Boolean) { fun setManualEnabled(value: Boolean) {
runtime.setManualEnabled(value) runtime.setManualEnabled(value)
} }

View File

@@ -78,6 +78,7 @@ class NodeRuntime(context: Context) {
val instanceId: StateFlow<String> = prefs.instanceId val instanceId: StateFlow<String> = prefs.instanceId
val displayName: StateFlow<String> = prefs.displayName val displayName: StateFlow<String> = prefs.displayName
val cameraEnabled: StateFlow<Boolean> = prefs.cameraEnabled val cameraEnabled: StateFlow<Boolean> = prefs.cameraEnabled
val preventSleep: StateFlow<Boolean> = prefs.preventSleep
val wakeWords: StateFlow<List<String>> = prefs.wakeWords val wakeWords: StateFlow<List<String>> = prefs.wakeWords
val manualEnabled: StateFlow<Boolean> = prefs.manualEnabled val manualEnabled: StateFlow<Boolean> = prefs.manualEnabled
val manualHost: StateFlow<String> = prefs.manualHost val manualHost: StateFlow<String> = prefs.manualHost
@@ -145,6 +146,10 @@ class NodeRuntime(context: Context) {
prefs.setCameraEnabled(value) prefs.setCameraEnabled(value)
} }
fun setPreventSleep(value: Boolean) {
prefs.setPreventSleep(value)
}
fun setManualEnabled(value: Boolean) { fun setManualEnabled(value: Boolean) {
prefs.setManualEnabled(value) prefs.setManualEnabled(value)
} }

View File

@@ -41,6 +41,9 @@ class SecurePrefs(context: Context) {
private val _cameraEnabled = MutableStateFlow(prefs.getBoolean("camera.enabled", true)) private val _cameraEnabled = MutableStateFlow(prefs.getBoolean("camera.enabled", true))
val cameraEnabled: StateFlow<Boolean> = _cameraEnabled val cameraEnabled: StateFlow<Boolean> = _cameraEnabled
private val _preventSleep = MutableStateFlow(prefs.getBoolean("screen.preventSleep", true))
val preventSleep: StateFlow<Boolean> = _preventSleep
private val _manualEnabled = MutableStateFlow(prefs.getBoolean("bridge.manual.enabled", false)) private val _manualEnabled = MutableStateFlow(prefs.getBoolean("bridge.manual.enabled", false))
val manualEnabled: StateFlow<Boolean> = _manualEnabled val manualEnabled: StateFlow<Boolean> = _manualEnabled
@@ -74,6 +77,11 @@ class SecurePrefs(context: Context) {
_cameraEnabled.value = value _cameraEnabled.value = value
} }
fun setPreventSleep(value: Boolean) {
prefs.edit().putBoolean("screen.preventSleep", value).apply()
_preventSleep.value = value
}
fun setManualEnabled(value: Boolean) { fun setManualEnabled(value: Boolean) {
prefs.edit().putBoolean("bridge.manual.enabled", value).apply() prefs.edit().putBoolean("bridge.manual.enabled", value).apply()
_manualEnabled.value = value _manualEnabled.value = value

View File

@@ -46,6 +46,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
val instanceId by viewModel.instanceId.collectAsState() val instanceId by viewModel.instanceId.collectAsState()
val displayName by viewModel.displayName.collectAsState() val displayName by viewModel.displayName.collectAsState()
val cameraEnabled by viewModel.cameraEnabled.collectAsState() val cameraEnabled by viewModel.cameraEnabled.collectAsState()
val preventSleep by viewModel.preventSleep.collectAsState()
val wakeWords by viewModel.wakeWords.collectAsState() val wakeWords by viewModel.wakeWords.collectAsState()
val isConnected by viewModel.isConnected.collectAsState() val isConnected by viewModel.isConnected.collectAsState()
val manualEnabled by viewModel.manualEnabled.collectAsState() val manualEnabled by viewModel.manualEnabled.collectAsState()
@@ -155,6 +156,17 @@ fun SettingsSheet(viewModel: MainViewModel) {
item { HorizontalDivider() } item { HorizontalDivider() }
item { Text("Screen") }
item {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) {
Switch(checked = preventSleep, onCheckedChange = viewModel::setPreventSleep)
Text(if (preventSleep) "Prevent Sleep" else "Allow Sleep")
}
}
item { Text("Keeps the screen awake while Clawdis is open.") }
item { HorizontalDivider() }
item { Text("Bridge") } item { Text("Bridge") }
item { Text("Status: $statusText") } item { Text("Status: $statusText") }
item { if (serverName != null) Text("Server: $serverName") } item { if (serverName != null) Text("Server: $serverName") }

View File

@@ -1,9 +1,12 @@
import SwiftUI import SwiftUI
import UIKit
struct RootCanvas: View { struct RootCanvas: View {
@Environment(NodeAppModel.self) private var appModel @Environment(NodeAppModel.self) private var appModel
@Environment(VoiceWakeManager.self) private var voiceWake @Environment(VoiceWakeManager.self) private var voiceWake
@Environment(\.scenePhase) private var scenePhase
@AppStorage(VoiceWakePreferences.enabledKey) private var voiceWakeEnabled: Bool = false @AppStorage(VoiceWakePreferences.enabledKey) private var voiceWakeEnabled: Bool = false
@AppStorage("screen.preventSleep") private var preventSleep: Bool = true
@State private var presentedSheet: PresentedSheet? @State private var presentedSheet: PresentedSheet?
@State private var voiceWakeToastText: String? @State private var voiceWakeToastText: String?
@State private var toastDismissTask: Task<Void, Never>? @State private var toastDismissTask: Task<Void, Never>?
@@ -63,6 +66,9 @@ struct RootCanvas: View {
} }
} }
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
.onAppear { self.updateIdleTimer() }
.onChange(of: self.preventSleep) { _, _ in self.updateIdleTimer() }
.onChange(of: self.scenePhase) { _, _ in self.updateIdleTimer() }
.onChange(of: self.voiceWake.lastTriggeredCommand) { _, newValue in .onChange(of: self.voiceWake.lastTriggeredCommand) { _, newValue in
guard let newValue else { return } guard let newValue else { return }
let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -83,6 +89,7 @@ struct RootCanvas: View {
} }
} }
.onDisappear { .onDisappear {
UIApplication.shared.isIdleTimerDisabled = false
self.toastDismissTask?.cancel() self.toastDismissTask?.cancel()
self.toastDismissTask = nil self.toastDismissTask = nil
} }
@@ -104,6 +111,10 @@ struct RootCanvas: View {
return .disconnected return .disconnected
} }
private func updateIdleTimer() {
UIApplication.shared.isIdleTimerDisabled = (self.scenePhase == .active && self.preventSleep)
}
} }
private struct OverlayButton: View { private struct OverlayButton: View {

View File

@@ -21,6 +21,7 @@ struct SettingsTab: View {
@AppStorage("node.instanceId") private var instanceId: String = UUID().uuidString @AppStorage("node.instanceId") private var instanceId: String = UUID().uuidString
@AppStorage("voiceWake.enabled") private var voiceWakeEnabled: Bool = false @AppStorage("voiceWake.enabled") private var voiceWakeEnabled: Bool = false
@AppStorage("camera.enabled") private var cameraEnabled: Bool = true @AppStorage("camera.enabled") private var cameraEnabled: Bool = true
@AppStorage("screen.preventSleep") private var preventSleep: Bool = true
@AppStorage("bridge.preferredStableID") private var preferredBridgeStableID: String = "" @AppStorage("bridge.preferredStableID") private var preferredBridgeStableID: String = ""
@AppStorage("bridge.lastDiscoveredStableID") private var lastDiscoveredBridgeStableID: String = "" @AppStorage("bridge.lastDiscoveredStableID") private var lastDiscoveredBridgeStableID: String = ""
@AppStorage("bridge.manual.enabled") private var manualBridgeEnabled: Bool = false @AppStorage("bridge.manual.enabled") private var manualBridgeEnabled: Bool = false
@@ -73,6 +74,13 @@ struct SettingsTab: View {
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
Section("Screen") {
Toggle("Prevent Sleep", isOn: self.$preventSleep)
Text("Keeps the screen awake while Clawdis is open.")
.font(.footnote)
.foregroundStyle(.secondary)
}
Section("Bridge") { Section("Bridge") {
LabeledContent("Discovery", value: self.bridgeController.discoveryStatusText) LabeledContent("Discovery", value: self.bridgeController.discoveryStatusText)
LabeledContent("Status", value: self.appModel.bridgeStatusText) LabeledContent("Status", value: self.appModel.bridgeStatusText)