Skip to content

Commit 6e13796

Browse files
committed
dev(ui): add dueDate field with Long timestamp for scheduling
1 parent 15eabed commit 6e13796

File tree

10 files changed

+136
-28
lines changed

10 files changed

+136
-28
lines changed

app/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ android {
115115
compileOptions {
116116
sourceCompatibility = JavaVersion.VERSION_11
117117
targetCompatibility = JavaVersion.VERSION_11
118+
119+
isCoreLibraryDesugaringEnabled = true
118120
}
119121
buildFeatures {
120122
compose = true
@@ -191,6 +193,8 @@ dependencies {
191193

192194
// Kotlin reflection library
193195
implementation(libs.kotlin.reflect)
196+
197+
coreLibraryDesugaring(libs.desugar.jdk.libs)
194198
}
195199

196200
project(":").tasks.named("buildAppDebug") {

app/src/main/java/com/flowintent/workspace/ui/TaskListScreen.kt

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import androidx.compose.material.icons.filled.Category
1717
import androidx.compose.material.icons.filled.DashboardCustomize
1818
import androidx.compose.material.icons.filled.Doorbell
1919
import androidx.compose.material3.Card
20-
import androidx.compose.material3.Checkbox
2120
import androidx.compose.material3.Icon
2221
import androidx.compose.material3.IconButton
2322
import androidx.compose.material3.Text
@@ -118,15 +117,13 @@ private fun ListCardContent(viewModel: TaskViewModel = hiltViewModel()) {
118117
val list = remember(taskList) { taskList.toMutableStateList() }
119118
var draggingItem by remember { mutableStateOf<DragInfo?>(null) }
120119
var itemHeight by remember { mutableStateOf(50.dp) }
121-
val isSelectionMode = viewModel.isSelectionMode.collectAsState()
122120

123121
LazyColumn(
124122
modifier = Modifier.fillMaxSize(),
125123
horizontalAlignment = Alignment.CenterHorizontally
126124
) {
127125
itemsIndexed(list, key = { index: Int, task: Task -> task.uid }) { index, task ->
128126
val isDragging = draggingItem?.index == index
129-
val isSelected = viewModel.selectedTasks[task.uid] ?: false
130127
Box(
131128
modifier = Modifier
132129
.graphicsLayer {
@@ -180,37 +177,33 @@ private fun ListCardContent(viewModel: TaskViewModel = hiltViewModel()) {
180177
modifier = Modifier.padding(start = 12.dp, top = 12.dp, end = 12.dp),
181178
verticalAlignment = Alignment.CenterVertically
182179
) {
183-
if (isSelectionMode.value) {
184-
Checkbox(
185-
checked = isSelected,
186-
onCheckedChange = { viewModel.toggleSelection(task.uid) },
187-
modifier = Modifier.padding(start = 8.dp)
188-
)
189-
}
190180
SwipeableCard(
191181
task = task,
192182
onDelete = { viewModel.deleteTask(task) },
193183
onEdit = { viewModel.setUpdateTaskId(task.uid) },
194184
onHeightChange = { itemHeight = it },
195185
) {
186+
val isExpanded = viewModel.expandedMap[task.uid] ?: false
196187
Column(
197188
modifier = Modifier
198189
.fillMaxWidth()
199-
.padding(12.dp)
190+
.padding(horizontal = 12.dp)
200191
) {
201192
Text(
202193
text = task.title,
203194
fontSize = 16.sp,
204195
fontFamily = FontFamily.SansSerif,
205196
fontWeight = FontWeight.Bold
206197
)
207-
Text(
208-
text = task.content.asString(),
209-
modifier = Modifier.padding(top = 12.dp),
210-
fontSize = 16.sp,
211-
fontFamily = FontFamily.SansSerif,
212-
fontWeight = FontWeight.Bold
213-
)
198+
if (isExpanded) {
199+
Text(
200+
text = task.content.asString(),
201+
modifier = Modifier.padding(top = 8.dp),
202+
fontSize = 16.sp,
203+
fontFamily = FontFamily.SansSerif,
204+
fontWeight = FontWeight.Bold
205+
)
206+
}
214207
}
215208
}
216209
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.flowintent.workspace.ui.button
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.border
5+
import androidx.compose.foundation.clickable
6+
import androidx.compose.foundation.layout.Box
7+
import androidx.compose.foundation.layout.size
8+
import androidx.compose.foundation.shape.CircleShape
9+
import androidx.compose.material.icons.Icons
10+
import androidx.compose.material.icons.filled.Check
11+
import androidx.compose.material3.Icon
12+
import androidx.compose.material3.MaterialTheme
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.ui.Alignment
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.draw.clip
17+
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.unit.dp
19+
20+
@Composable
21+
fun CustomRadioButton(
22+
selected: Boolean,
23+
onClick: () -> Unit,
24+
modifier: Modifier = Modifier
25+
) {
26+
val selectedColor = Color(0xFF42A5F5)
27+
28+
Box(
29+
modifier = modifier
30+
.size(20.dp)
31+
.clip(CircleShape)
32+
.background(
33+
if (selected) selectedColor else Color.Transparent
34+
)
35+
.border(
36+
width = 2.dp,
37+
color = if (selected) selectedColor
38+
else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
39+
shape = CircleShape
40+
)
41+
.clickable { onClick() },
42+
contentAlignment = Alignment.Center
43+
) {
44+
if (selected) {
45+
Icon(
46+
imageVector = Icons.Default.Check,
47+
contentDescription = "Selected",
48+
tint = Color.White,
49+
modifier = Modifier.size(12.dp)
50+
)
51+
}
52+
}
53+
}

app/src/main/java/com/flowintent/workspace/ui/dialog/OpenTaskDialog.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ fun OpenTaskDialog(
6464
taskType = TaskType.LOCAL_TASKS,
6565
cardColor = colorPicker.next().toArgbCompat(),
6666
iconColor = 0xFFFFFFFF.toInt(),
67-
textColor = 0xFF000000.toInt()
67+
textColor = 0xFF000000.toInt(),
68+
dueDate = System.currentTimeMillis()
6869
)
6970
)
7071
} else {

app/src/main/java/com/flowintent/workspace/ui/swipe/SwipeableCard.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fun SwipeableCard(
5454
val maxSwipe = with(LocalDensity.current) { 200.dp.toPx() }
5555

5656
val cardHeight by animateDpAsState(
57-
targetValue = if (isExpanded) 100.dp else 50.dp,
57+
targetValue = if (isExpanded) 80.dp else 50.dp,
5858
animationSpec = tween(300),
5959
label = "cardHeight"
6060
)

app/src/main/java/com/flowintent/workspace/ui/swipe/SwipeableCardContent.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
11
package com.flowintent.workspace.ui.swipe
22

33
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Row
45
import androidx.compose.foundation.layout.fillMaxHeight
56
import androidx.compose.foundation.layout.fillMaxSize
67
import androidx.compose.foundation.layout.padding
78
import androidx.compose.foundation.layout.width
89
import androidx.compose.foundation.shape.RoundedCornerShape
910
import androidx.compose.material3.Card
1011
import androidx.compose.material3.CardDefaults
12+
import androidx.compose.material3.MaterialTheme
1113
import androidx.compose.material3.Text
1214
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.collectAsState
1316
import androidx.compose.ui.Alignment
1417
import androidx.compose.ui.Modifier
1518
import androidx.compose.ui.graphics.Color
1619
import androidx.compose.ui.text.style.TextAlign
1720
import androidx.compose.ui.unit.dp
21+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
1822
import com.flowintent.core.db.Task
23+
import com.flowintent.workspace.ui.button.CustomRadioButton
24+
import com.flowintent.workspace.ui.vm.TaskViewModel
25+
import com.flowintent.workspace.util.getRelativeDayLabel
1926

2027
@Composable
2128
fun SwipeableCardContent(
2229
task: Task,
23-
content: @Composable () -> Unit
30+
content: @Composable () -> Unit,
31+
viewModel: TaskViewModel = hiltViewModel()
2432
) {
33+
val isSelected = viewModel.selectedTasks[task.uid] ?: false
34+
2535
Box(modifier = Modifier.fillMaxSize()) {
2636
// UID bar
2737
Card(
@@ -45,7 +55,22 @@ fun SwipeableCardContent(
4555
.padding(start = 48.dp),
4656
contentAlignment = Alignment.CenterStart
4757
) {
48-
content()
58+
Row(verticalAlignment = Alignment.CenterVertically) {
59+
CustomRadioButton(
60+
selected = isSelected,
61+
onClick = { viewModel.toggleSelection(task.uid) },
62+
modifier = Modifier.padding(start = 8.dp)
63+
)
64+
content()
65+
}
66+
Text(
67+
text = getRelativeDayLabel(task.dueDate),
68+
modifier = Modifier
69+
.align(Alignment.BottomEnd)
70+
.padding(8.dp),
71+
style = MaterialTheme.typography.labelSmall,
72+
color = MaterialTheme.colorScheme.onSurfaceVariant
73+
)
4974
}
5075
}
5176
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.flowintent.workspace.util
2+
3+
import java.text.SimpleDateFormat
4+
import java.util.Calendar
5+
import java.util.Locale
6+
7+
fun getRelativeDayLabel(dueDateMillis: Long): String {
8+
val now = Calendar.getInstance()
9+
val due = Calendar.getInstance().apply { timeInMillis = dueDateMillis }
10+
11+
val today = now.get(Calendar.DAY_OF_YEAR)
12+
val dueDay = due.get(Calendar.DAY_OF_YEAR)
13+
val diff = dueDay - today
14+
15+
return when (diff) {
16+
0 -> "Today"
17+
1 -> "Tomorrow"
18+
-1 -> "Yesterday"
19+
else -> {
20+
val format = SimpleDateFormat("dd MMM", Locale.getDefault())
21+
format.format(due.time)
22+
}
23+
}
24+
}

core/src/main/java/com/flowintent/core/db/Task.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ data class Task(
1212
@ColumnInfo("task_type") var taskType: TaskType = TaskType.LOCAL_TASKS,
1313
@ColumnInfo("card_color") var cardColor: Int,
1414
@ColumnInfo("icon_color") var iconColor: Int,
15-
@ColumnInfo("text_color") var textColor: Int = -1
15+
@ColumnInfo("text_color") var textColor: Int = -1,
16+
@ColumnInfo("due_date") var dueDate: Long
1617
)

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ activityCompose = "1.11.0"
44
agp = "8.11.2"
55
coreSplashscreen = "1.0.1"
66
datastorePreferences = "1.1.7"
7+
desugar_jdk_libs = "2.1.5"
78
firebaseBom = "34.3.0"
89
gson = "2.13.2"
910
hilt = "2.57.1"
@@ -59,6 +60,7 @@ androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", v
5960
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
6061
androidx-ui = { module = "androidx.compose.ui:ui" }
6162
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
63+
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
6264
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
6365
firebase-auth = { module = "com.google.firebase:firebase-auth" }
6466
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }

test/src/test/java/com/flowintent/test/TaskRepositoryTest.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class TaskRepositoryTest {
4242
taskType = TaskType.LOCAL_TASKS,
4343
cardColor = 0xFF6200EE.toInt(),
4444
iconColor = 0xFFFFFFFF.toInt(),
45-
textColor = 0xFF000000.toInt()
45+
textColor = 0xFF000000.toInt(),
46+
dueDate = System.currentTimeMillis()
4647
),
4748
Task(
4849
uid = 2,
@@ -51,7 +52,8 @@ class TaskRepositoryTest {
5152
taskType = TaskType.LOCAL_TASKS,
5253
cardColor = 0xFF03DAC5.toInt(),
5354
iconColor = 0xFFFFFFFF.toInt(),
54-
textColor = 0xFF000000.toInt()
55+
textColor = 0xFF000000.toInt(),
56+
dueDate = System.currentTimeMillis()
5557
)
5658
)
5759
`when`(toDoDao.getAllTasks()).thenReturn(flowOf(expectedTasks))
@@ -73,7 +75,8 @@ class TaskRepositoryTest {
7375
taskType = TaskType.LOCAL_TASKS,
7476
cardColor = 0xFF6200EE.toInt(),
7577
iconColor = 0xFFFFFFFF.toInt(),
76-
textColor = 0xFF000000.toInt()
78+
textColor = 0xFF000000.toInt(),
79+
dueDate = System.currentTimeMillis()
7780
)
7881

7982
// Act: Call the repository method
@@ -94,7 +97,8 @@ class TaskRepositoryTest {
9497
taskType = TaskType.LOCAL_TASKS,
9598
cardColor = 0xFF6200EE.toInt(),
9699
iconColor = 0xFFFFFFFF.toInt(),
97-
textColor = 0xFF000000.toInt()
100+
textColor = 0xFF000000.toInt(),
101+
dueDate = System.currentTimeMillis()
98102
)
99103
`when`(toDoDao.findByTaskName(taskName)).thenReturn(expectedTask)
100104

@@ -115,7 +119,8 @@ class TaskRepositoryTest {
115119
taskType = TaskType.LOCAL_TASKS,
116120
cardColor = 0xFF6200EE.toInt(),
117121
iconColor = 0xFFFFFFFF.toInt(),
118-
textColor = 0xFF000000.toInt()
122+
textColor = 0xFF000000.toInt(),
123+
dueDate = System.currentTimeMillis()
119124
)
120125
val expectedRowsAffected = 1
121126
`when`(toDoDao.delete(task)).thenReturn(expectedRowsAffected)

0 commit comments

Comments
 (0)