package com.tt.honeyDue.widget import android.content.Context import android.content.Intent import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.GlanceTheme import androidx.glance.action.ActionParameters import androidx.glance.action.actionParametersOf import androidx.glance.action.clickable import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetReceiver import androidx.glance.appwidget.action.ActionCallback import androidx.glance.appwidget.action.actionRunCallback import androidx.glance.appwidget.lazy.LazyColumn import androidx.glance.appwidget.lazy.items import androidx.glance.appwidget.provideContent import androidx.glance.background import androidx.glance.currentState import androidx.glance.layout.Alignment import androidx.glance.layout.Box import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.Spacer import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.height import androidx.glance.layout.padding import androidx.glance.layout.width import androidx.glance.state.GlanceStateDefinition import androidx.glance.state.PreferencesGlanceStateDefinition import androidx.glance.text.FontWeight import androidx.glance.text.Text import androidx.glance.text.TextStyle import androidx.glance.unit.ColorProvider import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import kotlinx.serialization.json.Json /** * Medium widget showing a list of upcoming tasks * Size: 4x2 */ class HoneyDueMediumWidget : GlanceAppWidget() { override val stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition private val json = Json { ignoreUnknownKeys = true } override suspend fun provideGlance(context: Context, id: GlanceId) { provideContent { GlanceTheme { MediumWidgetContent() } } } @Composable private fun MediumWidgetContent() { val prefs = currentState() val overdueCount = prefs[intPreferencesKey("overdue_count")] ?: 0 val dueSoonCount = prefs[intPreferencesKey("due_soon_count")] ?: 0 val tasksJson = prefs[stringPreferencesKey("tasks_json")] ?: "[]" val tasks = try { json.decodeFromString>(tasksJson).take(5) } catch (e: Exception) { emptyList() } Box( modifier = GlanceModifier .fillMaxSize() .background(Color(0xFFFFF8E7)) // Cream background .padding(12.dp) ) { Column( modifier = GlanceModifier.fillMaxSize() ) { // Header Row( modifier = GlanceModifier .fillMaxWidth() .clickable(actionRunCallback()), verticalAlignment = Alignment.CenterVertically ) { Text( text = "honeyDue", style = TextStyle( color = ColorProvider(Color(0xFF07A0C3)), fontSize = 18.sp, fontWeight = FontWeight.Bold ) ) Spacer(modifier = GlanceModifier.width(8.dp)) // Badge for overdue if (overdueCount > 0) { Box( modifier = GlanceModifier .background(Color(0xFFDD1C1A)) .padding(horizontal = 6.dp, vertical = 2.dp), contentAlignment = Alignment.Center ) { Text( text = "$overdueCount overdue", style = TextStyle( color = ColorProvider(Color.White), fontSize = 10.sp, fontWeight = FontWeight.Medium ) ) } } } Spacer(modifier = GlanceModifier.height(8.dp)) // Task list if (tasks.isEmpty()) { Box( modifier = GlanceModifier .fillMaxSize() .clickable(actionRunCallback()), contentAlignment = Alignment.Center ) { Text( text = "No upcoming tasks", style = TextStyle( color = ColorProvider(Color(0xFF888888)), fontSize = 14.sp ) ) } } else { LazyColumn( modifier = GlanceModifier.fillMaxSize() ) { items(tasks) { task -> TaskListItem(task = task) } } } } } } @Composable private fun TaskListItem(task: WidgetTask) { val taskIdKey = ActionParameters.Key("task_id") Row( modifier = GlanceModifier .fillMaxWidth() .padding(vertical = 4.dp) .clickable( actionRunCallback( actionParametersOf(taskIdKey to task.id) ) ), verticalAlignment = Alignment.CenterVertically ) { // Priority indicator Box( modifier = GlanceModifier .width(4.dp) .height(32.dp) .background(getPriorityColor(task.priorityLevel)) ) {} Spacer(modifier = GlanceModifier.width(8.dp)) Column( modifier = GlanceModifier.fillMaxWidth() ) { Text( text = task.title, style = TextStyle( color = ColorProvider(Color(0xFF1A1A1A)), fontSize = 13.sp, fontWeight = FontWeight.Medium ), maxLines = 1 ) Row { Text( text = task.residenceName, style = TextStyle( color = ColorProvider(Color(0xFF666666)), fontSize = 11.sp ), maxLines = 1 ) if (task.dueDate != null) { Text( text = " • ${task.dueDate}", style = TextStyle( color = ColorProvider( if (task.isOverdue) Color(0xFFDD1C1A) else Color(0xFF666666) ), fontSize = 11.sp ) ) } } } } } private fun getPriorityColor(level: Int): Color { return when (level) { 4 -> Color(0xFFDD1C1A) // Urgent - Red 3 -> Color(0xFFF5A623) // High - Amber 2 -> Color(0xFF07A0C3) // Medium - Primary else -> Color(0xFF888888) // Low - Gray } } } /** * Action to open a specific task */ class OpenTaskAction : ActionCallback { override suspend fun onAction( context: Context, glanceId: GlanceId, parameters: ActionParameters ) { val taskId = parameters[ActionParameters.Key("task_id")] val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) intent?.let { it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP if (taskId != null) { it.putExtra("navigate_to_task", taskId) } context.startActivity(it) } } } /** * Receiver for the medium widget */ class HoneyDueMediumWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = HoneyDueMediumWidget() }