This commit is contained in:
Moritz 2026-02-15 17:00:55 +01:00
parent 8b7b2c26c0
commit 5ad7c6cee8
2 changed files with 26 additions and 72 deletions

View file

@ -12,7 +12,6 @@ import android.provider.Settings
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -60,6 +59,7 @@ import java.util.Calendar
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.example.helios_alarm_clock.data.AlarmEntity import com.example.helios_alarm_clock.data.AlarmEntity
import com.example.helios_alarm_clock.service.KtorService
import com.example.helios_alarm_clock.ui.theme.HeliosTheme import com.example.helios_alarm_clock.ui.theme.HeliosTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -73,6 +73,7 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
requestPermissions() requestPermissions()
KtorService.start(this)
setContent { setContent {
HeliosTheme { HeliosTheme {
@ -114,7 +115,6 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun MainScreen(viewModel: MainViewModel = hiltViewModel()) { fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
val alarms by viewModel.alarms.collectAsStateWithLifecycle() val alarms by viewModel.alarms.collectAsStateWithLifecycle()
val serverRunning by viewModel.serverRunning.collectAsStateWithLifecycle()
var showAddDialog by remember { mutableStateOf(false) } var showAddDialog by remember { mutableStateOf(false) }
if (showAddDialog) { if (showAddDialog) {
@ -154,11 +154,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
) { ) {
ServerStatusCard( ServerStatusCard(
ipAddress = viewModel.ipAddress, ipAddress = viewModel.ipAddress,
port = viewModel.port, port = viewModel.port
isRunning = serverRunning,
onToggle = {
if (serverRunning) viewModel.stopServer() else viewModel.startServer()
}
) )
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
@ -204,18 +200,8 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
@Composable @Composable
fun ServerStatusCard( fun ServerStatusCard(
ipAddress: String, ipAddress: String,
port: Int, port: Int
isRunning: Boolean,
onToggle: () -> Unit
) { ) {
val statusColor by animateColorAsState(
targetValue = if (isRunning)
MaterialTheme.colorScheme.primary
else
MaterialTheme.colorScheme.onSurfaceVariant,
label = "statusColor"
)
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
@ -223,42 +209,28 @@ fun ServerStatusCard(
) )
) { ) {
Column(modifier = Modifier.padding(16.dp)) { Column(modifier = Modifier.padding(16.dp)) {
Row( Text(
modifier = Modifier.fillMaxWidth(), text = "HTTP Server",
horizontalArrangement = Arrangement.SpaceBetween, style = MaterialTheme.typography.titleMedium,
verticalAlignment = Alignment.CenterVertically fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "$ipAddress:$port",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
Surface(
shape = MaterialTheme.shapes.small,
color = MaterialTheme.colorScheme.primaryContainer
) { ) {
Column { Text(
Text( text = "Listening for connections",
text = "HTTP Server", modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Bold color = MaterialTheme.colorScheme.onPrimaryContainer
) )
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (isRunning) "$ipAddress:$port" else "Stopped",
style = MaterialTheme.typography.bodyLarge,
color = statusColor
)
}
FilledTonalButton(onClick = onToggle) {
Text(if (isRunning) "Stop" else "Start")
}
}
if (isRunning) {
Spacer(modifier = Modifier.height(8.dp))
Surface(
shape = MaterialTheme.shapes.small,
color = MaterialTheme.colorScheme.primaryContainer
) {
Text(
text = "Listening for connections",
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
} }
} }
} }

View file

@ -1,6 +1,5 @@
package com.example.helios_alarm_clock.ui package com.example.helios_alarm_clock.ui
import android.content.Context
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.helios_alarm_clock.data.AlarmDao import com.example.helios_alarm_clock.data.AlarmDao
@ -9,11 +8,8 @@ import com.example.helios_alarm_clock.service.KtorService
import com.example.helios_alarm_clock.util.AlarmScheduler import com.example.helios_alarm_clock.util.AlarmScheduler
import com.example.helios_alarm_clock.util.getLocalIpAddress import com.example.helios_alarm_clock.util.getLocalIpAddress
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Calendar import java.util.Calendar
@ -23,31 +19,17 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MainViewModel @Inject constructor( class MainViewModel @Inject constructor(
private val alarmDao: AlarmDao, private val alarmDao: AlarmDao,
private val alarmScheduler: AlarmScheduler, private val alarmScheduler: AlarmScheduler
@param:ApplicationContext private val context: Context
) : ViewModel() { ) : ViewModel() {
val alarms: StateFlow<List<AlarmEntity>> = alarmDao.observeAll() val alarms: StateFlow<List<AlarmEntity>> = alarmDao.observeAll()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
private val _serverRunning = MutableStateFlow(false)
val serverRunning: StateFlow<Boolean> = _serverRunning.asStateFlow()
val ipAddress: String val ipAddress: String
get() = getLocalIpAddress() ?: "No network" get() = getLocalIpAddress() ?: "No network"
val port: Int = KtorService.PORT val port: Int = KtorService.PORT
fun startServer() {
KtorService.start(context)
_serverRunning.value = true
}
fun stopServer() {
KtorService.stop(context)
_serverRunning.value = false
}
fun createAlarm(hour: Int, minute: Int, label: String) { fun createAlarm(hour: Int, minute: Int, label: String) {
viewModelScope.launch { viewModelScope.launch {
val now = Calendar.getInstance() val now = Calendar.getInstance()