Fix build failures from rebrand: restore pbxproj exceptions, fix Kotlin casing, move missed source dirs

- Restore 6 missing PBXFileSystemSynchronizedBuildFileExceptionSet entries
  and exceptions arrays on 5 root groups (lost during sed rename)
- Rename extension WidgetIconView.swift to avoid stringsdata collision
  (original had different names: MyCribIconView vs CaseraIconView)
- Rename CaseraExtension.entitlements → HoneyDueExtension.entitlements
- Fix Kotlin object casing: honeyDueShareCodec → HoneyDueShareCodec,
  honeyDuePackageType → HoneyDuePackageType
- Move missed Kotlin source dirs (jsMain, webMain, androidMain/com/casera)
  to com/tt/honeyDue
- Rename remaining Casera widget files to HoneyDue
- Rename CaseraTests.swift → HoneyDueTests.swift

All 4 projects (Go API, iOS, Android, Web) now compile clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-07 06:58:56 -06:00
parent 1e2adf7660
commit d3b6b14e78
29 changed files with 83 additions and 341 deletions
@@ -0,0 +1,367 @@
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.cornerRadius
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.size
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.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
/**
* Large widget showing task list with stats and interactive actions (Pro only)
* Size: 4x4
*/
class HoneyDueLargeWidget : GlanceAppWidget() {
override val stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition
private val json = Json { ignoreUnknownKeys = true }
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
GlanceTheme {
LargeWidgetContent()
}
}
}
@Composable
private fun LargeWidgetContent() {
val prefs = currentState<Preferences>()
val overdueCount = prefs[intPreferencesKey("overdue_count")] ?: 0
val dueSoonCount = prefs[intPreferencesKey("due_soon_count")] ?: 0
val inProgressCount = prefs[intPreferencesKey("in_progress_count")] ?: 0
val totalCount = prefs[intPreferencesKey("total_tasks_count")] ?: 0
val tasksJson = prefs[stringPreferencesKey("tasks_json")] ?: "[]"
val isProUser = prefs[stringPreferencesKey("is_pro_user")] == "true"
val tasks = try {
json.decodeFromString<List<WidgetTask>>(tasksJson).take(8)
} catch (e: Exception) {
emptyList()
}
Box(
modifier = GlanceModifier
.fillMaxSize()
.background(Color(0xFFFFF8E7)) // Cream background
.padding(16.dp)
) {
Column(
modifier = GlanceModifier.fillMaxSize()
) {
// Header with logo
Row(
modifier = GlanceModifier
.fillMaxWidth()
.clickable(actionRunCallback<OpenAppAction>()),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "honeyDue",
style = TextStyle(
color = ColorProvider(Color(0xFF07A0C3)),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = GlanceModifier.width(8.dp))
Text(
text = "Tasks",
style = TextStyle(
color = ColorProvider(Color(0xFF666666)),
fontSize = 14.sp
)
)
}
Spacer(modifier = GlanceModifier.height(12.dp))
// Stats row
Row(
modifier = GlanceModifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
StatBox(
count = overdueCount,
label = "Overdue",
color = Color(0xFFDD1C1A),
bgColor = Color(0xFFFFEBEB)
)
Spacer(modifier = GlanceModifier.width(8.dp))
StatBox(
count = dueSoonCount,
label = "Due Soon",
color = Color(0xFFF5A623),
bgColor = Color(0xFFFFF4E0)
)
Spacer(modifier = GlanceModifier.width(8.dp))
StatBox(
count = inProgressCount,
label = "Active",
color = Color(0xFF07A0C3),
bgColor = Color(0xFFE0F4F8)
)
}
Spacer(modifier = GlanceModifier.height(12.dp))
// Divider
Box(
modifier = GlanceModifier
.fillMaxWidth()
.height(1.dp)
.background(Color(0xFFE0E0E0))
) {}
Spacer(modifier = GlanceModifier.height(8.dp))
// Task list
if (tasks.isEmpty()) {
Box(
modifier = GlanceModifier
.fillMaxSize()
.clickable(actionRunCallback<OpenAppAction>()),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "All caught up!",
style = TextStyle(
color = ColorProvider(Color(0xFF07A0C3)),
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
)
Text(
text = "No tasks need attention",
style = TextStyle(
color = ColorProvider(Color(0xFF888888)),
fontSize = 12.sp
)
)
}
}
} else {
LazyColumn(
modifier = GlanceModifier.fillMaxSize()
) {
items(tasks) { task ->
InteractiveTaskItem(
task = task,
isProUser = isProUser
)
}
}
}
}
}
}
@Composable
private fun StatBox(count: Int, label: String, color: Color, bgColor: Color) {
Box(
modifier = GlanceModifier
.background(bgColor)
.padding(horizontal = 12.dp, vertical = 8.dp)
.cornerRadius(8.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = count.toString(),
style = TextStyle(
color = ColorProvider(color),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Text(
text = label,
style = TextStyle(
color = ColorProvider(color),
fontSize = 10.sp
)
)
}
}
}
@Composable
private fun InteractiveTaskItem(task: WidgetTask, isProUser: Boolean) {
val taskIdKey = ActionParameters.Key<Int>("task_id")
Row(
modifier = GlanceModifier
.fillMaxWidth()
.padding(vertical = 6.dp)
.clickable(
actionRunCallback<OpenTaskAction>(
actionParametersOf(taskIdKey to task.id)
)
),
verticalAlignment = Alignment.CenterVertically
) {
// Priority indicator
Box(
modifier = GlanceModifier
.width(4.dp)
.height(40.dp)
.background(getPriorityColor(task.priorityLevel))
) {}
Spacer(modifier = GlanceModifier.width(8.dp))
// Task details
Column(
modifier = GlanceModifier.defaultWeight()
) {
Text(
text = task.title,
style = TextStyle(
color = ColorProvider(Color(0xFF1A1A1A)),
fontSize = 14.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
)
)
}
}
}
// Action button (Pro only)
if (isProUser) {
Box(
modifier = GlanceModifier
.size(32.dp)
.background(Color(0xFF07A0C3))
.cornerRadius(16.dp)
.clickable(
actionRunCallback<CompleteTaskAction>(
actionParametersOf(taskIdKey to task.id)
)
),
contentAlignment = Alignment.Center
) {
Text(
text = "",
style = TextStyle(
color = ColorProvider(Color.White),
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
)
}
}
}
}
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 complete a task from the widget (Pro only)
*/
class CompleteTaskAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val taskId = parameters[ActionParameters.Key<Int>("task_id")] ?: return
// Send broadcast to app to complete the task
val intent = Intent("com.tt.honeyDue.COMPLETE_TASK").apply {
putExtra("task_id", taskId)
setPackage(context.packageName)
}
context.sendBroadcast(intent)
// Update widget after action
withContext(Dispatchers.Main) {
HoneyDueLargeWidget().update(context, glanceId)
}
}
}
/**
* Receiver for the large widget
*/
class HoneyDueLargeWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = HoneyDueLargeWidget()
}