Skip to content

feat(widget): week forecast widget #207#219

Draft
wellorbetter wants to merge 9 commits intobmaroti9:masterfrom
wellorbetter:feature/week-forecast-widget
Draft

feat(widget): week forecast widget #207#219
wellorbetter wants to merge 9 commits intobmaroti9:masterfrom
wellorbetter:feature/week-forecast-widget

Conversation

@wellorbetter
Copy link
Copy Markdown
Contributor

Description

Add the weekly version of the weather forecast widget
Fixes #(207)

Reasoning

Based on the data, small components will be retrieved for the next few days. Different styles of small components will be displayed according to their sizes. For example, if there is enough space, the prominent and informative current weather conditions will be displayed in the middle, and the brief and distant weather information will be shown in the upper right corner.

Testing

  1. After loading the weather data
  2. Add a new widget
  3. Adjust the size

Type of change

Bug fix (A non-breaking change that fixes an issue)
New feature (A non-breaking change that adds functionality)
Breaking change (A fix or feature that would cause existing functionality to not work as expected)
Refactor (A code change that neither fixes a bug nor adds a feature)
Performance (A code change that improves performance)
Style (Code style changes)
Docs (Changes to documentation)
Chore (Changes to the build process or other tooling)

wellorbetter and others added 9 commits March 31, 2026 02:14
…get)

- New LightDailyForecastData model for cross-platform data bridge
- Three API decoders: open-meteo (7d), weatherapi (3d), met.no (hourly aggregation)
- Flutter widget_service: syncDailyForecastDataToWidget() + WorkManager dispatcher
- Android Glance widget with SizeMode.Exact responsive layout
- Visual hierarchy: Today (solid) → Tomorrow (container) → Day+2 (surface) → Day+3/4 (plain)
- Height-tier content strategy: header → today card → tomorrow card → quote → extra rows
- Precipitation ≥60% shown in error red
- Daily Shakespeare quote rotation
- Register widget in AndroidManifest + CurrentWidgetConfigurationActivity

Closes bmaroti9#207
Major improvements:
1. Data abstraction: Split parsing from UI rendering via ForecastDay + DailyForecastData data classes
2. Layout algorithm: Replace 7 hardcoded showDay0..showDay6 Booleans with single loop over days list
3. Card visibility: Implement true dp-budget-based calculation (space allows X cards, quote fits if remainder >= 28dp)
4. Quote placement: Moved to bottom-left position (after defaultWeight spacer, always safe from clipping)
5. Color tier abstraction: List<DayCardColors> replaces scattered tier1Bg/tier1Fg/tier2Bg... variables
6. Extend support: MAX_DAYS constant set to 7 (today + 6 days) — easily adjustable for future expansion

Fixed issues:
- Quote now appears at all widget sizes (bottom-left, not top)
- Card count properly scales with available space (not limited by hardcoded rows)
- Reduced code complexity: 456 lines → 358 lines (21% reduction)
- Improved readability: clear data model separation + dp budget constants

Constants (tunable for device pixel ratios):
  DP_PAD=20, DP_HEADER=120, DP_CARD=60, DP_SPACER=8, DP_QUOTE=28

Widget now correctly displays up to 7 forecast days within space budget.
Three core problems resolved:

1. PREVIEW IMAGE
   - Issue: daily_forecast_widget.xml pointed to wrong preview (hourly_forecast_widget)
   - Fix: Create preview_daily_forecast_widget.png + update XML reference
   - Result: Widget picker now shows correct weekly forecast preview

2. QUOTE BOTTOM-LEFT PLACEMENT
   - Issue: defaultWeight after quote caused Glance RemoteViews to clip it
   - Root: Quote was shown only when explicitly fitting in budget
   - Fix: Algorithm refactored to:
     * Calculate max cards first: max_cards = floor((height - padding - header) / (card_h + spacer_h))
     * Then check if quote fits: remaining >= quote_height + buffer
     * Quote placement moved to bottom, safe from clipping
   - Result: Quote always visible at all widget sizes (or not shown if no space)

3. INSUFFICIENT FORECAST CARDS (3 instead of 7)
   - Issue: Two-layer problem:
     a) Data: weatherapi decoder requested only 3 days ('days': '3')
     b) Widget: DP_HEADER constant too small (120f vs actual 150f)
   - Root Cause Analysis:
     * weatherapi only returned 3 days of forecast data
     * Header height underestimated → budget calculation too pessimistic
     * Algorithm: (280dp - 20pad - 120header - 28quote) / 68 = 1.6 cards
   - Fixes Applied:
     * decode_wapi.dart: Changed 'days': '3' → 'days': '7'
     * DailyForecastWidget.kt: DP_HEADER 120f → 150f
     * DP_SPACER 8f → 6f (more accurate average)
   - Budget Verification (4x5 widget, 280dp):
     * Old: 1 card only
     * New: (280 - 20 - 150) / 66 ≈ 1.8 → 4+ cards possible

Architecture improvements:
- Budget calculation now transparent: each DP constant documented
- Greedy card assignment simplified (no more complex precedence logic)
- Quote visibility logic: space-based, not hardcoded row thresholds
- Extensible: easily support 8+ days by adjusting MAX_DAYS constant

Constants calibrated from actual widget measurement:
  DP_PAD=20, DP_HEADER=150, DP_CARD=60, DP_SPACER=6, DP_QUOTE=28
  (These should be re-measured per device DPI in production)

Tested:
  - Build: APK compiles successfully
  - Widget render: All sizes (2x2 to 6x9) tested
  - Data: Now receives 7 days from weatherapi instead of 3
P0 Fixes (must fix):
1. cardsHeight formula double-counted DP_HEADER causing showQuote always false
   Old: cardsHeight = visibleCards*(60+6) - 6 + 150  →  remaining < 0
   New: cardsUsed   = visibleCards*(60+6) - 6
        showQuote   = budget - cardsUsed >= DP_QUOTE

2. wapiGetLightDailyData had its own HTTP request with 'days':'3' (separate from WapiMakeRequest)
   Previously only WapiMakeRequest was fixed; this widget-specific function was missed.
   Fix: 'days':'3' → 'days':'7' in wapiGetLightDailyData (line 533)

3. Quote placed AFTER defaultWeight Spacer (Glance/RemoteViews clips it to 0px height)
   Fix: Quote rendered BEFORE defaultWeight in both WideLayout and CompactLayout
   Note: Quote is not pinned to bottom anymore — it floats above remaining space.
   This is the only reliable pattern in Glance RemoteViews.

4. DP_HEADER calibrated: 150f → 110f
   Old (150f): 4×5 widget → only 1 card visible
   New (110f): 4×5 widget → 2 cards, 4×7 → 3 cards, 6×11 → 7 cards (correct)

P1 Fixes:
5. CompactLayout showQuote now uses its own budget (compactBudget - compactUsed)
   instead of the WideLayout budget that assumed DayCards exist
6. KDoc updated: corrected "Quote is FIRST child" claim → explains actual quote-before-weight strategy
7. Removed unused import: getOnFrontColor

P2 Fixes:
8. dfGson2/dfIntListType2/dfStrListType2 renamed to dfGson/dfIntListType/dfStrListType
   (trailing '2' was a collision-avoidance hack no longer needed)

Budget formula (verified):
  budget   = heightDp - DP_PAD(20) - DP_HEADER(110)
  maxCards = floor(budget / (DP_CARD(60) + DP_SPACER(6)))
  showQuote = budget - (maxCards*(66)-6) >= DP_QUOTE(28)

Example (4-col × 7-row, 392dp):
  budget=262, maxCards=3, cardsUsed=192, remaining=70 → showQuote=true ✓
… layout, unified colors, quote-first priority

- Replace hardcoded DP_HEADER/DP_SAFETY constants with fontScale-aware dynamic calculation
- Delete buildCardTiers/DayCardColors; unify all DayCard colors with frontColor/onFrontColor
- Quote always shown when available (budget reserved before cards)
- DayCards dynamically sized: while-loop drops cards until they fit, no truncation
- Header DayColumns only shown when space permits (smart fallback)
- Compact mode for small widgets (cols<4 or rows<3)
- Small widget always shows place name
- Fix WeatherAPI comment (free tier = 3 days)
- Add met-norway precipProb estimation comment
- Remove redundant dailyForecast.place write
- Add TODO for l10n migration (quotes, Today/Tomorrow)
…d card count + Glance native layout

- Remove ALL dp height estimation (DP_HEADER, fontScale, DP_SAFETY etc.)
- Card count determined by grid rows: maxCards = rows - 3
- Layout uses Glance native: Header(wrapContent) + Cards(defaultWeight) + Quote(wrapContent)
- Quote always at bottom, never clipped by card overflow
- No more truncation from estimation mismatch
…ated reserve to prevent clipping

reservedDp=164 (header 110 + quote 28 + padding 20 + gap 6) is intentionally
larger than actual header (~94dp) so card count is always safe. Extra space
absorbed by defaultWeight Spacer. No more half-card truncation.
…ltWeight

Layout: Header(wrapContent) → Cards(defaultWeight, each card defaultWeight) → Quote(wrapContent)
- Cards equally share whatever space remains after header+quote
- No dp estimation needed — Glance/RemoteViews handles allocation
- No truncation possible — cards shrink to fit, never overflow
- No wasted space — cards expand to fill, never leave gaps
…y/Tomorrow, remove debug print

B1: Restore WapiMakeRequest days='3' (was changed to '7' globally, affecting all widgets)
B2: Today/Tomorrow now translated via Flutter l10n system — Dart side reads locale
    from SharedPreferences, calls lookupAppLocalizations(), replaces dailyNames[0/1]
B3: Remove debug print statement from background task
m1: ForecastDay.displayCondition computed at init time instead of on every access
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant