build: S12 five widgets after reference, per-widget style config

- weather card (3x2 to 4x5), pill, tilted pill, bar with cookie badge, classic
- configuration screen on add and via reconfigure: location, theme, palette, opacity, live preview
- wallpaper (Material You), weather and five fixed palettes, all pairs WCAG AA
- Glance 1.2: picker previews on Android 15+, debug widget gallery
- DB v2 migration for widget theme and opacity

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-17 14:24:52 +02:00
co-authored by Claude Opus 5.5
parent 8eb9544189
commit 0c42d8570b
25 changed files with 1459 additions and 227 deletions
+2 -1
View File
@@ -12,7 +12,8 @@ Stack as in architecture.md. App in `wetter/`. Screenshots are rendered on the J
| S15 Preview | 2026-10-17 | done | – | – |
| S04 Day detail | 2026-10-17 | done | – | tiles need day-specific values (noon), not today's |
| S08/S09 Settings | 2026-10-17 | done | notification settings (no notifications yet) | – |
| S12 Widgets | 2026-10-17 | partial | config activity (pick a location), 4×4 and 4×1 types, dynamic-colour style; **no screenshot** (Glance does not render in Robolectric), only verified to compile | widgets cannot draw shaders → the scene is pre-rendered to a bitmap |
| S12 Widgets | 2026-10-17 | done | – | Glance 1.1 could not render outside the launcher; upgraded to 1.2 for compose() and picker previews, checked on the phone via a debug gallery |
| S12 Widgets (first pass) | 2026-10-17 | superseded | config activity (pick a location), 4×4 and 4×1 types, dynamic-colour style; **no screenshot** (Glance does not render in Robolectric), only verified to compile | widgets cannot draw shaders → the scene is pre-rendered to a bitmap |
| S05, S06, S10, S11, S13 | – | not started | card detail, radar map, reordering, alert detail, notifications | – |
Not yet verified on a real device: location without Play Services, network on device, widgets on the launcher, WorkManager timing.
+70
View File
@@ -114,6 +114,76 @@ object WColor {
val aqiBar6 = Color(0xFF8E24AA)
val uvDotLow = Color(0xFF5CCF8A)
val uvDotOff = Color(0xFF6B5A52)
val widgetOceanLightBg = Color(0xFFCCE4F0)
val widgetOceanLightOn = Color(0xFF0F2733)
val widgetOceanLightAccent = Color(0xFF20546F)
val widgetOceanLightPanel = Color(0xFFF9FAFB)
val widgetOceanLightOnpanel = Color(0xFF192124)
val widgetOceanLightBadge = Color(0xFFA8D1E6)
val widgetOceanLightOnbadge = Color(0xFF0F2733)
val widgetOceanDarkBg = Color(0xFF194257)
val widgetOceanDarkOn = Color(0xFFDBECF5)
val widgetOceanDarkAccent = Color(0xFFB0D5E8)
val widgetOceanDarkPanel = Color(0xFF0E1315)
val widgetOceanDarkOnpanel = Color(0xFFE7ECEF)
val widgetOceanDarkBadge = Color(0xFF1F516B)
val widgetOceanDarkOnbadge = Color(0xFFDFEEF6)
val widgetForestLightBg = Color(0xFFD2E9DA)
val widgetForestLightOn = Color(0xFF162D1D)
val widgetForestLightAccent = Color(0xFF2E603F)
val widgetForestLightPanel = Color(0xFFF9FBFA)
val widgetForestLightOnpanel = Color(0xFF1B221D)
val widgetForestLightBadge = Color(0xFFB3DBC0)
val widgetForestLightOnbadge = Color(0xFF162D1D)
val widgetForestDarkBg = Color(0xFF244C32)
val widgetForestDarkOn = Color(0xFFE0F0E5)
val widgetForestDarkAccent = Color(0xFFBADEC6)
val widgetForestDarkPanel = Color(0xFF101411)
val widgetForestDarkOnpanel = Color(0xFFE8EDEA)
val widgetForestDarkBadge = Color(0xFF2D5D3D)
val widgetForestDarkOnbadge = Color(0xFFE3F2E8)
val widgetSunLightBg = Color(0xFFF7E4C5)
val widgetSunLightOn = Color(0xFF3A2808)
val widgetSunLightAccent = Color(0xFF7D5612)
val widgetSunLightPanel = Color(0xFFFBFAF9)
val widgetSunLightOnpanel = Color(0xFF272117)
val widgetSunLightBadge = Color(0xFFF1D29D)
val widgetSunLightOnbadge = Color(0xFF3A2808)
val widgetSunDarkBg = Color(0xFF62430E)
val widgetSunDarkOn = Color(0xFFF9EDD7)
val widgetSunDarkAccent = Color(0xFFF2D6A6)
val widgetSunDarkPanel = Color(0xFF17130D)
val widgetSunDarkOnpanel = Color(0xFFF0ECE5)
val widgetSunDarkBadge = Color(0xFF785311)
val widgetSunDarkOnbadge = Color(0xFFFAEFDB)
val widgetLavenderLightBg = Color(0xFFDBCFED)
val widgetLavenderLightOn = Color(0xFF1F1230)
val widgetLavenderLightAccent = Color(0xFF422768)
val widgetLavenderLightPanel = Color(0xFFFAF9FB)
val widgetLavenderLightOnpanel = Color(0xFF1E1A23)
val widgetLavenderLightBadge = Color(0xFFC3AEE0)
val widgetLavenderLightOnbadge = Color(0xFF1F1230)
val widgetLavenderDarkBg = Color(0xFF341F51)
val widgetLavenderDarkOn = Color(0xFFE6DEF2)
val widgetLavenderDarkAccent = Color(0xFFC8B5E3)
val widgetLavenderDarkPanel = Color(0xFF110F15)
val widgetLavenderDarkOnpanel = Color(0xFFEAE7EE)
val widgetLavenderDarkBadge = Color(0xFF402664)
val widgetLavenderDarkOnbadge = Color(0xFFE9E1F4)
val widgetGraphiteLightBg = Color(0xFFDBDDE1)
val widgetGraphiteLightOn = Color(0xFF1E2124)
val widgetGraphiteLightAccent = Color(0xFF42464D)
val widgetGraphiteLightPanel = Color(0xFFFAFAFA)
val widgetGraphiteLightOnpanel = Color(0xFF1E1E1F)
val widgetGraphiteLightBadge = Color(0xFFC2C6CB)
val widgetGraphiteLightOnbadge = Color(0xFF1E2124)
val widgetGraphiteDarkBg = Color(0xFF34373D)
val widgetGraphiteDarkOn = Color(0xFFE6E8EA)
val widgetGraphiteDarkAccent = Color(0xFFC8CBD0)
val widgetGraphiteDarkPanel = Color(0xFF111212)
val widgetGraphiteDarkOnpanel = Color(0xFFEAEBEB)
val widgetGraphiteDarkBadge = Color(0xFF3F444A)
val widgetGraphiteDarkOnbadge = Color(0xFFE9EAEC)
}
object WType {
+231 -1
View File
@@ -106,7 +106,77 @@
"aqi-bar-5": "#e53935",
"aqi-bar-6": "#8e24aa",
"uv-dot-low": "#5ccf8a",
"uv-dot-off": "#6b5a52"
"uv-dot-off": "#6b5a52",
"widget-ocean-light-bg": "#cce4f0",
"widget-ocean-light-on": "#0f2733",
"widget-ocean-light-accent": "#20546f",
"widget-ocean-light-panel": "#f9fafb",
"widget-ocean-light-onpanel": "#192124",
"widget-ocean-light-badge": "#a8d1e6",
"widget-ocean-light-onbadge": "#0f2733",
"widget-ocean-dark-bg": "#194257",
"widget-ocean-dark-on": "#dbecf5",
"widget-ocean-dark-accent": "#b0d5e8",
"widget-ocean-dark-panel": "#0e1315",
"widget-ocean-dark-onpanel": "#e7ecef",
"widget-ocean-dark-badge": "#1f516b",
"widget-ocean-dark-onbadge": "#dfeef6",
"widget-forest-light-bg": "#d2e9da",
"widget-forest-light-on": "#162d1d",
"widget-forest-light-accent": "#2e603f",
"widget-forest-light-panel": "#f9fbfa",
"widget-forest-light-onpanel": "#1b221d",
"widget-forest-light-badge": "#b3dbc0",
"widget-forest-light-onbadge": "#162d1d",
"widget-forest-dark-bg": "#244c32",
"widget-forest-dark-on": "#e0f0e5",
"widget-forest-dark-accent": "#badec6",
"widget-forest-dark-panel": "#101411",
"widget-forest-dark-onpanel": "#e8edea",
"widget-forest-dark-badge": "#2d5d3d",
"widget-forest-dark-onbadge": "#e3f2e8",
"widget-sun-light-bg": "#f7e4c5",
"widget-sun-light-on": "#3a2808",
"widget-sun-light-accent": "#7d5612",
"widget-sun-light-panel": "#fbfaf9",
"widget-sun-light-onpanel": "#272117",
"widget-sun-light-badge": "#f1d29d",
"widget-sun-light-onbadge": "#3a2808",
"widget-sun-dark-bg": "#62430e",
"widget-sun-dark-on": "#f9edd7",
"widget-sun-dark-accent": "#f2d6a6",
"widget-sun-dark-panel": "#17130d",
"widget-sun-dark-onpanel": "#f0ece5",
"widget-sun-dark-badge": "#785311",
"widget-sun-dark-onbadge": "#faefdb",
"widget-lavender-light-bg": "#dbcfed",
"widget-lavender-light-on": "#1f1230",
"widget-lavender-light-accent": "#422768",
"widget-lavender-light-panel": "#faf9fb",
"widget-lavender-light-onpanel": "#1e1a23",
"widget-lavender-light-badge": "#c3aee0",
"widget-lavender-light-onbadge": "#1f1230",
"widget-lavender-dark-bg": "#341f51",
"widget-lavender-dark-on": "#e6def2",
"widget-lavender-dark-accent": "#c8b5e3",
"widget-lavender-dark-panel": "#110f15",
"widget-lavender-dark-onpanel": "#eae7ee",
"widget-lavender-dark-badge": "#402664",
"widget-lavender-dark-onbadge": "#e9e1f4",
"widget-graphite-light-bg": "#dbdde1",
"widget-graphite-light-on": "#1e2124",
"widget-graphite-light-accent": "#42464d",
"widget-graphite-light-panel": "#fafafa",
"widget-graphite-light-onpanel": "#1e1e1f",
"widget-graphite-light-badge": "#c2c6cb",
"widget-graphite-light-onbadge": "#1e2124",
"widget-graphite-dark-bg": "#34373d",
"widget-graphite-dark-on": "#e6e8ea",
"widget-graphite-dark-accent": "#c8cbd0",
"widget-graphite-dark-panel": "#111212",
"widget-graphite-dark-onpanel": "#eaebeb",
"widget-graphite-dark-badge": "#3f444a",
"widget-graphite-dark-onbadge": "#e9eaec"
},
"pairs": [
[
@@ -396,6 +466,166 @@
"nowcast-dot",
"card",
"ui"
],
[
"widget-ocean-light-on",
"widget-ocean-light-bg"
],
[
"widget-ocean-light-accent",
"widget-ocean-light-bg"
],
[
"widget-ocean-light-onpanel",
"widget-ocean-light-panel"
],
[
"widget-ocean-light-onbadge",
"widget-ocean-light-badge"
],
[
"widget-ocean-dark-on",
"widget-ocean-dark-bg"
],
[
"widget-ocean-dark-accent",
"widget-ocean-dark-bg"
],
[
"widget-ocean-dark-onpanel",
"widget-ocean-dark-panel"
],
[
"widget-ocean-dark-onbadge",
"widget-ocean-dark-badge"
],
[
"widget-forest-light-on",
"widget-forest-light-bg"
],
[
"widget-forest-light-accent",
"widget-forest-light-bg"
],
[
"widget-forest-light-onpanel",
"widget-forest-light-panel"
],
[
"widget-forest-light-onbadge",
"widget-forest-light-badge"
],
[
"widget-forest-dark-on",
"widget-forest-dark-bg"
],
[
"widget-forest-dark-accent",
"widget-forest-dark-bg"
],
[
"widget-forest-dark-onpanel",
"widget-forest-dark-panel"
],
[
"widget-forest-dark-onbadge",
"widget-forest-dark-badge"
],
[
"widget-sun-light-on",
"widget-sun-light-bg"
],
[
"widget-sun-light-accent",
"widget-sun-light-bg"
],
[
"widget-sun-light-onpanel",
"widget-sun-light-panel"
],
[
"widget-sun-light-onbadge",
"widget-sun-light-badge"
],
[
"widget-sun-dark-on",
"widget-sun-dark-bg"
],
[
"widget-sun-dark-accent",
"widget-sun-dark-bg"
],
[
"widget-sun-dark-onpanel",
"widget-sun-dark-panel"
],
[
"widget-sun-dark-onbadge",
"widget-sun-dark-badge"
],
[
"widget-lavender-light-on",
"widget-lavender-light-bg"
],
[
"widget-lavender-light-accent",
"widget-lavender-light-bg"
],
[
"widget-lavender-light-onpanel",
"widget-lavender-light-panel"
],
[
"widget-lavender-light-onbadge",
"widget-lavender-light-badge"
],
[
"widget-lavender-dark-on",
"widget-lavender-dark-bg"
],
[
"widget-lavender-dark-accent",
"widget-lavender-dark-bg"
],
[
"widget-lavender-dark-onpanel",
"widget-lavender-dark-panel"
],
[
"widget-lavender-dark-onbadge",
"widget-lavender-dark-badge"
],
[
"widget-graphite-light-on",
"widget-graphite-light-bg"
],
[
"widget-graphite-light-accent",
"widget-graphite-light-bg"
],
[
"widget-graphite-light-onpanel",
"widget-graphite-light-panel"
],
[
"widget-graphite-light-onbadge",
"widget-graphite-light-badge"
],
[
"widget-graphite-dark-on",
"widget-graphite-dark-bg"
],
[
"widget-graphite-dark-accent",
"widget-graphite-dark-bg"
],
[
"widget-graphite-dark-onpanel",
"widget-graphite-dark-panel"
],
[
"widget-graphite-dark-onbadge",
"widget-graphite-dark-badge"
]
],
"font": {
+44 -44
View File
@@ -1,44 +1,44 @@
feature,area,priority,original,clone,notes
Current conditions hero (temp feels-like high/low condition),home,must,yes,yes,
Condition-based animated background,home,must,yes,yes,original artwork not reusable; draw own
Hourly forecast strip (24h+),home,must,yes,yes,fixed position
10-day forecast list,home,must,yes,yes,
Daily detail view (tap a day),home,should,yes,yes,guess: exists in original
Precipitation card with chance and amount,cards,must,yes,yes,
Wind card (speed direction gusts),cards,must,yes,yes,
UV index card,cards,must,yes,yes,
Humidity / dew point card,cards,must,yes,yes,
Pressure card,cards,should,yes,yes,
Visibility card,cards,should,yes,yes,
Sunrise / sunset card,cards,must,yes,yes,
Air quality (AQI) card,cards,should,yes,yes,Open-Meteo air-quality API (CAMS)
Pollen card,cards,could,yes,yes,Open-Meteo pollen (Europe only)
Drag to reorder cards,customize,should,yes,no,
Card order saved per city,customize,could,yes,no,
Location search by name or postal code,locations,must,yes,yes,Open-Meteo geocoding
Current location via GPS,locations,must,yes,yes,no Google Play Services; use platform LocationManager
Saved locations list,locations,must,yes,yes,
Swipe to delete a location,locations,must,yes,yes,
Swipe/pager between saved cities,locations,should,yes,yes,
Temperature unit (default C F),settings,must,yes,yes,
Units for precipitation wind pressure visibility,settings,should,yes,yes,
Theme setting (system light dark),settings,should,yes,yes,
Precipitation map with 6h nowcast and timeline scrubber,map,should,yes,no,RainViewer or DWD radar tiles + OSM base map
Pan and zoom map,map,should,yes,no,
Severe weather alerts banner on top,alerts,must,yes,no,DWD / MeteoAlarm feeds
Severe weather notification,notifications,should,yes,no,background worker
Evening tomorrow-forecast notification,notifications,could,yes,no,
Precipitation-starting notification per city,notifications,could,yes,no,needs minute nowcast
Homescreen widget,widgets,must,yes,partial,"2 of 5 widget types (Now 2x1/2x2, Forecast 4x2/4x3); no config activity yet"
AI weather brief summary,ai,could,yes,partial,"template-based text or local LLM; no Gemini; rule-based brief, no LLM"
Weather vibrations on animations,polish,could,yes,no,
Tablet two-column layout,layout,could,yes,partial,content capped at 720dp and centred; no true two-pane
Sync saved locations across devices via Google account,sync,could,yes,skip,no Google account by design
Personal insights from Calendar and Gmail,ai,could,yes,skip,needs Google data access; against project goal
Google nowcasting / MetNet models,data,could,yes,skip,proprietary model; use public forecast data
At a Glance integration,widgets,could,yes,skip,Pixel launcher system feature
Widget sizes small medium large with hourly/daily,widgets,must,yes,partial,Glance; resizable; small/medium/wide/tall done; 4x4 full and 4x1 glance missing
Widget dynamic Material You colours and condition background,widgets,should,yes,partial,condition background only; dynamic colour style missing
Widget tap opens city / refresh on schedule,widgets,must,yes,yes,
Nowcast card (precip starting with 15-min dot chart),cards,should,yes,yes,Open-Meteo minutely_15
Location preview with Save,locations,should,yes,yes,S15
feature,area,priority,original,clone,notes
Current conditions hero (temp feels-like high/low condition),home,must,yes,yes,
Condition-based animated background,home,must,yes,yes,original artwork not reusable; draw own
Hourly forecast strip (24h+),home,must,yes,yes,fixed position
10-day forecast list,home,must,yes,yes,
Daily detail view (tap a day),home,should,yes,yes,guess: exists in original
Precipitation card with chance and amount,cards,must,yes,yes,
Wind card (speed direction gusts),cards,must,yes,yes,
UV index card,cards,must,yes,yes,
Humidity / dew point card,cards,must,yes,yes,
Pressure card,cards,should,yes,yes,
Visibility card,cards,should,yes,yes,
Sunrise / sunset card,cards,must,yes,yes,
Air quality (AQI) card,cards,should,yes,yes,Open-Meteo air-quality API (CAMS)
Pollen card,cards,could,yes,yes,Open-Meteo pollen (Europe only)
Drag to reorder cards,customize,should,yes,no,
Card order saved per city,customize,could,yes,no,
Location search by name or postal code,locations,must,yes,yes,Open-Meteo geocoding
Current location via GPS,locations,must,yes,yes,no Google Play Services; use platform LocationManager
Saved locations list,locations,must,yes,yes,
Swipe to delete a location,locations,must,yes,yes,
Swipe/pager between saved cities,locations,should,yes,yes,
Temperature unit (default C F),settings,must,yes,yes,
Units for precipitation wind pressure visibility,settings,should,yes,yes,
Theme setting (system light dark),settings,should,yes,yes,
Precipitation map with 6h nowcast and timeline scrubber,map,should,yes,no,RainViewer or DWD radar tiles + OSM base map
Pan and zoom map,map,should,yes,no,
Severe weather alerts banner on top,alerts,must,yes,no,DWD / MeteoAlarm feeds
Severe weather notification,notifications,should,yes,no,background worker
Evening tomorrow-forecast notification,notifications,could,yes,no,
Precipitation-starting notification per city,notifications,could,yes,no,needs minute nowcast
Homescreen widget,widgets,must,yes,yes,5 widget types with config screen
AI weather brief summary,ai,could,yes,partial,"template-based text or local LLM; no Gemini; rule-based brief, no LLM"
Weather vibrations on animations,polish,could,yes,no,
Tablet two-column layout,layout,could,yes,partial,content capped at 720dp and centred; no true two-pane
Sync saved locations across devices via Google account,sync,could,yes,skip,no Google account by design
Personal insights from Calendar and Gmail,ai,could,yes,skip,needs Google data access; against project goal
Google nowcasting / MetNet models,data,could,yes,skip,proprietary model; use public forecast data
At a Glance integration,widgets,could,yes,skip,Pixel launcher system feature
Widget sizes small medium large with hourly/daily,widgets,must,yes,yes,Glance
Widget dynamic Material You colours and condition background,widgets,should,yes,yes,condition background only
Widget tap opens city / refresh on schedule,widgets,must,yes,yes,
Nowcast card (precip starting with 15-min dot chart),cards,should,yes,yes,Open-Meteo minutely_15
Location preview with Save,locations,should,yes,yes,S15
1 feature area priority original clone notes
2 Current conditions hero (temp feels-like high/low condition) home must yes yes
3 Condition-based animated background home must yes yes original artwork not reusable; draw own
4 Hourly forecast strip (24h+) home must yes yes fixed position
5 10-day forecast list home must yes yes
6 Daily detail view (tap a day) home should yes yes guess: exists in original
7 Precipitation card with chance and amount cards must yes yes
8 Wind card (speed direction gusts) cards must yes yes
9 UV index card cards must yes yes
10 Humidity / dew point card cards must yes yes
11 Pressure card cards should yes yes
12 Visibility card cards should yes yes
13 Sunrise / sunset card cards must yes yes
14 Air quality (AQI) card cards should yes yes Open-Meteo air-quality API (CAMS)
15 Pollen card cards could yes yes Open-Meteo pollen (Europe only)
16 Drag to reorder cards customize should yes no
17 Card order saved per city customize could yes no
18 Location search by name or postal code locations must yes yes Open-Meteo geocoding
19 Current location via GPS locations must yes yes no Google Play Services; use platform LocationManager
20 Saved locations list locations must yes yes
21 Swipe to delete a location locations must yes yes
22 Swipe/pager between saved cities locations should yes yes
23 Temperature unit (default C F) settings must yes yes
24 Units for precipitation wind pressure visibility settings should yes yes
25 Theme setting (system light dark) settings should yes yes
26 Precipitation map with 6h nowcast and timeline scrubber map should yes no RainViewer or DWD radar tiles + OSM base map
27 Pan and zoom map map should yes no
28 Severe weather alerts banner on top alerts must yes no DWD / MeteoAlarm feeds
29 Severe weather notification notifications should yes no background worker
30 Evening tomorrow-forecast notification notifications could yes no
31 Precipitation-starting notification per city notifications could yes no needs minute nowcast
32 Homescreen widget widgets must yes partial yes 2 of 5 widget types (Now 2x1/2x2, Forecast 4x2/4x3); no config activity yet 5 widget types with config screen
33 AI weather brief summary ai could yes partial template-based text or local LLM; no Gemini; rule-based brief, no LLM
34 Weather vibrations on animations polish could yes no
35 Tablet two-column layout layout could yes partial content capped at 720dp and centred; no true two-pane
36 Sync saved locations across devices via Google account sync could yes skip no Google account by design
37 Personal insights from Calendar and Gmail ai could yes skip needs Google data access; against project goal
38 Google nowcasting / MetNet models data could yes skip proprietary model; use public forecast data
39 At a Glance integration widgets could yes skip Pixel launcher system feature
40 Widget sizes small medium large with hourly/daily widgets must yes partial yes Glance; resizable; small/medium/wide/tall done; 4x4 full and 4x1 glance missing Glance
41 Widget dynamic Material You colours and condition background widgets should yes partial yes condition background only; dynamic colour style missing condition background only
42 Widget tap opens city / refresh on schedule widgets must yes yes
43 Nowcast card (precip starting with 15-min dot chart) cards should yes yes Open-Meteo minutely_15
44 Location preview with Save locations should yes yes S15