Mobile: prevent sleep setting
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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") }
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user