Refactor tile layout to show recent cases
The tile layout has been refactored to display a list of recent cases instead of the currently active case. This change includes: - Removing the "current case" view and its associated logic. - Implementing a new `caseListContent` function to render a list of up to 8 recent cases. - Modifying the main `buildTileLayout` to integrate the new case list and a prominent "Neu" (New) button. - Updating the styling and click handling to reflect the new layout and functionality.
This commit is contained in:
@@ -14,8 +14,8 @@ private const val RESOURCES_VERSION = "1"
|
||||
|
||||
/**
|
||||
* Serves the single Doctate Tile. Reads a non-blocking snapshot from
|
||||
* [DoctateApp.caseStore] and renders the "glance at current case" view with
|
||||
* three tap regions. Refreshes are triggered externally via
|
||||
* [DoctateApp.caseStore] and renders the recent-cases list with an
|
||||
* edge-style "Neu" button. Refreshes are triggered externally via
|
||||
* `TileService.getUpdater(ctx).requestUpdate(...)` — the App wires that up
|
||||
* to the case-store flow.
|
||||
*/
|
||||
@@ -24,9 +24,9 @@ class DoctateTileService : TileService() {
|
||||
requestParams: RequestBuilders.TileRequest,
|
||||
): ListenableFuture<TileBuilders.Tile> {
|
||||
val app = applicationContext as DoctateApp
|
||||
val current = app.caseStore.currentCase
|
||||
val cases = app.caseStore.casesFlow.value
|
||||
val layout = LayoutElementBuilders.Layout.Builder()
|
||||
.setRoot(buildTileLayout(packageName, current))
|
||||
.setRoot(buildTileLayout(packageName, cases))
|
||||
.build()
|
||||
|
||||
val timeline = TimelineBuilders.Timeline.Builder()
|
||||
|
||||
@@ -4,15 +4,11 @@ import androidx.wear.protolayout.ActionBuilders
|
||||
import androidx.wear.protolayout.ColorBuilders.argb
|
||||
import androidx.wear.protolayout.DimensionBuilders.dp
|
||||
import androidx.wear.protolayout.DimensionBuilders.expand
|
||||
import androidx.wear.protolayout.LayoutElementBuilders
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.Box
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.Column
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.FontStyle
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.Row
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.Spacer
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.Text
|
||||
import androidx.wear.protolayout.LayoutElementBuilders.VERTICAL_ALIGN_BOTTOM
|
||||
@@ -22,7 +18,6 @@ import androidx.wear.protolayout.ModifiersBuilders.Clickable
|
||||
import androidx.wear.protolayout.ModifiersBuilders.Corner
|
||||
import androidx.wear.protolayout.ModifiersBuilders.Modifiers
|
||||
import androidx.wear.protolayout.ModifiersBuilders.Padding
|
||||
import androidx.wear.protolayout.TypeBuilders.StringProp
|
||||
import com.doctate.watch.domain.CaseEntry
|
||||
import com.doctate.watch.domain.displayText
|
||||
import com.doctate.watch.domain.isManual
|
||||
@@ -30,101 +25,98 @@ import com.doctate.watch.presentation.NavCommand
|
||||
|
||||
private const val CLICK_ID_LIST = "list"
|
||||
private const val CLICK_ID_NEW = "new"
|
||||
private const val CLICK_ID_CONTINUE = "continue"
|
||||
|
||||
/** Build the root layout element for the Tile. Returns the "current case" view or an
|
||||
* empty-state view depending on whether any case exists in [CaseStoreStub]. */
|
||||
internal fun buildTileLayout(packageName: String, current: CaseEntry?): LayoutElement {
|
||||
val column = Column.Builder()
|
||||
private const val MAX_VISIBLE_CASES = 8
|
||||
|
||||
/**
|
||||
* Build the root Tile layout: a vertical stack with the most recent cases
|
||||
* pinned just above an edge-style "Neu" button. The whole list region is a
|
||||
* single tap target — every line routes to the case list, regardless of
|
||||
* which case was tapped. Up to [MAX_VISIBLE_CASES] cases are shown; the
|
||||
* newest sits at the bottom (next to the edge button).
|
||||
*/
|
||||
internal fun buildTileLayout(packageName: String, cases: List<CaseEntry>): LayoutElement {
|
||||
return Column.Builder()
|
||||
.setWidth(expand())
|
||||
.setHeight(expand())
|
||||
.setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
|
||||
.addContent(topRow(packageName))
|
||||
.addContent(Spacer.Builder().setHeight(dp(8f)).build())
|
||||
.addContent(middleContent(packageName, current))
|
||||
.addContent(Spacer.Builder().setHeight(dp(8f)).build())
|
||||
.addContent(Spacer.Builder().setHeight(dp(10f)).build())
|
||||
.addContent(caseListContent(packageName, cases))
|
||||
.addContent(Spacer.Builder().setHeight(dp(4f)).build())
|
||||
.addContent(bottomNewButton(packageName))
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun caseListContent(packageName: String, cases: List<CaseEntry>): LayoutElement {
|
||||
val column = Column.Builder()
|
||||
.setWidth(expand())
|
||||
.setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
|
||||
|
||||
if (cases.isEmpty()) {
|
||||
column.addContent(
|
||||
Text.Builder()
|
||||
.setText("Noch kein Fall heute")
|
||||
.setFontStyle(FontStyle.Builder().setSize(sp(14f)).build())
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
val visible = cases.take(MAX_VISIBLE_CASES).asReversed()
|
||||
visible.forEachIndexed { index, case ->
|
||||
if (index > 0) {
|
||||
column.addContent(Spacer.Builder().setHeight(dp(2f)).build())
|
||||
}
|
||||
column.addContent(
|
||||
Text.Builder()
|
||||
.setText(formatLine(case))
|
||||
.setMaxLines(1)
|
||||
.setFontStyle(FontStyle.Builder().setSize(sp(13f)).build())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return Box.Builder()
|
||||
.setWidth(expand())
|
||||
.setHeight(expand())
|
||||
.setVerticalAlignment(VERTICAL_ALIGN_CENTER)
|
||||
.addContent(column)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun topRow(packageName: String): LayoutElement {
|
||||
val faelle = Text.Builder()
|
||||
.setText("☰ Fälle")
|
||||
.setFontStyle(FontStyle.Builder().setSize(sp(14f)).build())
|
||||
.setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
|
||||
.setVerticalAlignment(VERTICAL_ALIGN_BOTTOM)
|
||||
.setModifiers(
|
||||
Modifiers.Builder()
|
||||
.setClickable(launchClickable(CLICK_ID_LIST, packageName, NavCommand.OPEN_LIST))
|
||||
.setPadding(
|
||||
Padding.Builder().setStart(dp(8f)).setEnd(dp(8f))
|
||||
.setTop(dp(4f)).setBottom(dp(4f)).build(),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
// Row has no horizontal-alignment prop; use a Box with END alignment instead.
|
||||
return Box.Builder()
|
||||
.setWidth(expand())
|
||||
.setHorizontalAlignment(HORIZONTAL_ALIGN_END)
|
||||
.setVerticalAlignment(VERTICAL_ALIGN_CENTER)
|
||||
.addContent(faelle)
|
||||
.addContent(column.build())
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun middleContent(packageName: String, current: CaseEntry?): LayoutElement {
|
||||
val label = when {
|
||||
current == null -> "Noch kein Fall heute"
|
||||
else -> current.oneliner.displayText()
|
||||
?.let { if (current.oneliner.isManual()) "👤 $it" else it }
|
||||
?: "…"
|
||||
}
|
||||
val textBuilder = Text.Builder()
|
||||
.setText(label)
|
||||
.setMaxLines(3)
|
||||
.setFontStyle(FontStyle.Builder().setSize(sp(16f)).build())
|
||||
|
||||
val modifiersBuilder = Modifiers.Builder()
|
||||
.setPadding(
|
||||
Padding.Builder().setStart(dp(12f)).setEnd(dp(12f))
|
||||
.setTop(dp(6f)).setBottom(dp(6f)).build(),
|
||||
)
|
||||
if (current != null) {
|
||||
modifiersBuilder.setClickable(
|
||||
launchClickable(
|
||||
CLICK_ID_CONTINUE,
|
||||
packageName,
|
||||
NavCommand.OPEN_CONTINUE,
|
||||
caseId = current.caseId,
|
||||
),
|
||||
)
|
||||
}
|
||||
textBuilder.setModifiers(modifiersBuilder.build())
|
||||
return textBuilder.build()
|
||||
/** "👤 text" if doctor-authored, plain text if auto-generated, "…" placeholder otherwise. */
|
||||
private fun formatLine(case: CaseEntry): String {
|
||||
val text = case.oneliner.displayText() ?: return "…"
|
||||
return if (case.oneliner.isManual()) "👤 $text" else text
|
||||
}
|
||||
|
||||
private fun bottomNewButton(packageName: String): LayoutElement {
|
||||
val text = Text.Builder()
|
||||
.setText("● Neu")
|
||||
.setFontStyle(FontStyle.Builder().setSize(sp(14f)).build())
|
||||
.setFontStyle(
|
||||
FontStyle.Builder()
|
||||
.setSize(sp(14f))
|
||||
.setColor(argb(0xFF0A305F.toInt()))
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
return Box.Builder()
|
||||
.setWidth(expand())
|
||||
.setHeight(dp(36f))
|
||||
.setVerticalAlignment(VERTICAL_ALIGN_BOTTOM)
|
||||
.setHeight(dp(48f))
|
||||
.setVerticalAlignment(VERTICAL_ALIGN_CENTER)
|
||||
.setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
|
||||
.setModifiers(
|
||||
Modifiers.Builder()
|
||||
.setClickable(launchClickable(CLICK_ID_NEW, packageName, NavCommand.OPEN_NEW))
|
||||
.setBackground(
|
||||
Background.Builder()
|
||||
.setColor(argb(0xFF1F1F1F.toInt()))
|
||||
.setCorner(Corner.Builder().setRadius(dp(18f)).build())
|
||||
.setColor(argb(0xFFAAC7FF.toInt()))
|
||||
.setCorner(Corner.Builder().setRadius(dp(24f)).build())
|
||||
.build(),
|
||||
)
|
||||
.setPadding(
|
||||
|
||||
Reference in New Issue
Block a user