diff --git a/app/src/googleplay/java/org/tasks/wear/WearDataService.kt b/app/src/googleplay/java/org/tasks/wear/WearDataService.kt index 972393568..b33c7d275 100644 --- a/app/src/googleplay/java/org/tasks/wear/WearDataService.kt +++ b/app/src/googleplay/java/org/tasks/wear/WearDataService.kt @@ -1,5 +1,6 @@ package org.tasks.wear +import android.text.format.DateFormat import androidx.datastore.core.DataStore import androidx.lifecycle.lifecycleScope import com.google.android.horologist.annotations.ExperimentalHorologistApi @@ -58,6 +59,7 @@ class WearDataService : BaseGrpcDataService + is UiItem.Task -> { + val timestamp = if (preferences.groupMode == SORT_DUE && + (item.task.sortGroup + ?: 0) >= currentTimeMillis().startOfDay() + ) { + item.task.takeIf { it.hasDueTime() }?.let { + getTimeString(item.task.dueDate, is24HourTime) + } + } else if (item.task.hasDueDate()) { + getRelativeDateTime( + item.task.dueDate, + is24HourTime, + ) + } else { + null + } + GrpcProto.UiItem.newBuilder() .setType(ListItemType.Item) .setId(item.task.id) @@ -98,9 +120,13 @@ class WearService( if (item.task.title != null) { setTitle(item.task.title) } + if (timestamp != null) { + setTimestamp(timestamp) + } } .setRepeating(item.task.task.isRecurring) .build() + } } } ) diff --git a/wear-datalayer/src/main/proto/grpc.proto b/wear-datalayer/src/main/proto/grpc.proto index fa86dbfd4..b414df62d 100644 --- a/wear-datalayer/src/main/proto/grpc.proto +++ b/wear-datalayer/src/main/proto/grpc.proto @@ -21,6 +21,7 @@ message UiItem { bool hidden = 8; uint32 indent = 9; uint32 numSubtasks = 10; + optional string timestamp = 11; } message Tasks { diff --git a/wear/src/main/java/org/tasks/presentation/components/TaskCard.kt b/wear/src/main/java/org/tasks/presentation/components/TaskCard.kt index f0dcf38ab..a19501da4 100644 --- a/wear/src/main/java/org/tasks/presentation/components/TaskCard.kt +++ b/wear/src/main/java/org/tasks/presentation/components/TaskCard.kt @@ -1,9 +1,14 @@ package org.tasks.presentation.components +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,6 +26,7 @@ import androidx.wear.compose.material.Text @Composable fun TaskCard( text: String, + timestamp: String?, hidden: Boolean = false, numSubtasks: Int = 0, subtasksCollapsed: Boolean = false, @@ -44,26 +50,56 @@ fun TaskCard( maxLines = 2, overflow = TextOverflow.Ellipsis, color = contentColor, - modifier = Modifier.alpha(if (hidden) .6f else 1f).weight(1f), + modifier = Modifier + .alpha(if (hidden) .6f else 1f) + .weight(1f), ) if (numSubtasks > 0) { Button( onClick = toggleSubtasks, - colors = ButtonDefaults.outlinedButtonColors() + colors = ButtonDefaults.outlinedButtonColors(), + shape = if (timestamp.isNullOrBlank()) CircleShape else RoundedCornerShape(16.dp), + modifier = Modifier.wrapContentWidth(), ) { - Row( - verticalAlignment = Alignment.CenterVertically + Column( + horizontalAlignment = Alignment.End, ) { - Text( - text = numSubtasks.toString(), // TODO: use number formatter - color = contentColor, - ) - Chevron(subtasksCollapsed) + Timestamp(timestamp, contentColor) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = numSubtasks.toString(), // TODO: use number formatter + color = contentColor, + ) + Chevron(subtasksCollapsed) + } } } } else { + Timestamp(timestamp, contentColor) Spacer(modifier = Modifier.width(12.dp)) } } } } + +@Composable +private fun Timestamp( + timestamp: String?, + color: Color, +) { + if (timestamp.isNullOrBlank()) { + return + } + Text( + text = timestamp, + color = color, + style = MaterialTheme.typography.caption2, + modifier = Modifier + .alpha(.6f) + .padding(start = 4.dp), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) +} diff --git a/wear/src/main/java/org/tasks/presentation/screens/TaskEditScreen.kt b/wear/src/main/java/org/tasks/presentation/screens/TaskEditScreen.kt index e2897bbe8..204409819 100644 --- a/wear/src/main/java/org/tasks/presentation/screens/TaskEditScreen.kt +++ b/wear/src/main/java/org/tasks/presentation/screens/TaskEditScreen.kt @@ -58,7 +58,7 @@ fun TaskEditScreen( keyboardInputRequest.openKeyboard() } } - if (uiState.taskId == 0L) { + if (!uiState.loaded) { Box( modifier = Modifier.fillMaxRectangle(), contentAlignment = Alignment.Center, diff --git a/wear/src/main/java/org/tasks/presentation/screens/TaskEditViewModel.kt b/wear/src/main/java/org/tasks/presentation/screens/TaskEditViewModel.kt index 1872bfc81..1293254b4 100644 --- a/wear/src/main/java/org/tasks/presentation/screens/TaskEditViewModel.kt +++ b/wear/src/main/java/org/tasks/presentation/screens/TaskEditViewModel.kt @@ -17,6 +17,7 @@ import org.tasks.extensions.wearDataLayerRegistry import timber.log.Timber data class UiState( + val loaded: Boolean = false, val taskId: Long = 0, val completed: Boolean = false, val repeating: Boolean = false, @@ -27,9 +28,9 @@ data class UiState( @OptIn(ExperimentalHorologistApi::class) class TaskEditViewModel( applicationContext: Context, - private val taskId: Long, + taskId: Long, ) : ViewModel() { - private val _uiState = MutableStateFlow(UiState()) + private val _uiState = MutableStateFlow(UiState(taskId = taskId)) val uiState = _uiState.asStateFlow() private val registry = applicationContext.wearDataLayerRegistry(viewModelScope) @@ -54,6 +55,7 @@ class TaskEditViewModel( Timber.d("Received $task") _uiState.update { it.copy( + loaded = true, taskId = taskId, completed = task.completed, title = task.title, @@ -77,7 +79,7 @@ class TaskEditViewModel( wearService.saveTask( GrpcProto.SaveTaskRequest.newBuilder() .setTitle(state.title) - .setTaskId(taskId) + .setTaskId(state.taskId) .setCompleted(state.completed) .build() ) diff --git a/wear/src/main/java/org/tasks/presentation/screens/TaskListScreen.kt b/wear/src/main/java/org/tasks/presentation/screens/TaskListScreen.kt index 661d11b29..7bdf60711 100644 --- a/wear/src/main/java/org/tasks/presentation/screens/TaskListScreen.kt +++ b/wear/src/main/java/org/tasks/presentation/screens/TaskListScreen.kt @@ -112,6 +112,7 @@ fun TaskListScreen( } TaskCard( text = item.title, + timestamp = item.timestamp, hidden = item.hidden, subtasksCollapsed = item.collapsed, numSubtasks = item.numSubtasks,