package org.terst.doot.widget.ui import android.appwidget.AppWidgetManager import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.datastore.preferences.core.edit import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import okhttp3.OkHttpClient import org.terst.doot.widget.data.Keys import org.terst.doot.widget.data.WidgetRepository import org.terst.doot.widget.data.dataStore import org.terst.doot.widget.work.RefreshWorker class SettingsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appWidgetId = intent?.extras?.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) ?: AppWidgetManager.INVALID_APPWIDGET_ID setResult(RESULT_CANCELED) setContent { MaterialTheme(colorScheme = darkColorScheme()) { val scope = rememberCoroutineScope() var savedUrl by remember { mutableStateOf("") } var savedToken by remember { mutableStateOf("") } LaunchedEffect(Unit) { val prefs = dataStore.data.first() savedUrl = prefs[Keys.SERVER_URL] ?: "" savedToken = prefs[Keys.TOKEN] ?: "" } SettingsScreen( initialUrl = savedUrl, initialToken = savedToken, onSave = { url, token -> lifecycleScope.launch { saveAndFinish(url, token, appWidgetId) } }, onTest = { url, token, onResult -> scope.launch { val repo = WidgetRepository(OkHttpClient(), url.trimEnd('/'), token) repo.fetchRaw().fold( onSuccess = { onResult("✓ Connected (${it.items.size} items)") }, onFailure = { onResult("✗ ${it.message}") } ) } } ) } } } private suspend fun saveAndFinish(url: String, token: String, appWidgetId: Int) { dataStore.edit { prefs -> prefs[Keys.SERVER_URL] = url.trimEnd('/') prefs[Keys.TOKEN] = token } RefreshWorker.schedule(this) RefreshWorker.runOnce(this) if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { setResult(RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)) } else { setResult(RESULT_OK) } finish() } } @Composable fun SettingsScreen( initialUrl: String = "", initialToken: String = "", onSave: (url: String, token: String) -> Unit, onTest: (url: String, token: String, onResult: (String) -> Unit) -> Unit ) { var url by remember(initialUrl) { mutableStateOf(initialUrl) } var token by remember(initialToken) { mutableStateOf(initialToken) } var status by remember { mutableStateOf("") } var testing by remember { mutableStateOf(false) } Column( modifier = Modifier .fillMaxSize() .padding(24.dp), verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically) ) { Text("Doot Widget", style = MaterialTheme.typography.headlineMedium) Text( "Enter your doot server URL and the WIDGET_TOKEN from your .env file.", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) OutlinedTextField( value = url, onValueChange = { url = it }, label = { Text("Server URL") }, placeholder = { Text("https://doot.example.com") }, modifier = Modifier.fillMaxWidth(), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), singleLine = true ) OutlinedTextField( value = token, onValueChange = { token = it }, label = { Text("Widget Token") }, modifier = Modifier.fillMaxWidth(), visualTransformation = PasswordVisualTransformation(), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), singleLine = true ) if (status.isNotEmpty()) { Text( status, color = if (status.startsWith("✓")) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall ) } Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { OutlinedButton( onClick = { testing = true status = "Testing..." onTest(url, token) { result -> status = result testing = false } }, enabled = url.isNotBlank() && token.isNotBlank() && !testing ) { Text("Test") } Button( onClick = { onSave(url, token) }, enabled = url.isNotBlank() && token.isNotBlank() ) { Text("Save") } } } }