diff --git a/clients/wearos/.kotlin/sessions/kotlin-compiler-10087412367400182703.salive b/clients/wearos/.kotlin/sessions/kotlin-compiler-10087412367400182703.salive deleted file mode 100644 index e69de29..0000000 diff --git a/clients/wearos/app/src/main/java/com/doctate/watch/tile/DoctateTileService.kt b/clients/wearos/app/src/main/java/com/doctate/watch/tile/DoctateTileService.kt index 0f487f8..aebd5d6 100644 --- a/clients/wearos/app/src/main/java/com/doctate/watch/tile/DoctateTileService.kt +++ b/clients/wearos/app/src/main/java/com/doctate/watch/tile/DoctateTileService.kt @@ -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 { 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() diff --git a/clients/wearos/app/src/main/java/com/doctate/watch/tile/TileLayouts.kt b/clients/wearos/app/src/main/java/com/doctate/watch/tile/TileLayouts.kt index 7de99f6..24d2b9e 100644 --- a/clients/wearos/app/src/main/java/com/doctate/watch/tile/TileLayouts.kt +++ b/clients/wearos/app/src/main/java/com/doctate/watch/tile/TileLayouts.kt @@ -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): 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): 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(