summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SESSION_STATE.md200
1 files changed, 200 insertions, 0 deletions
diff --git a/SESSION_STATE.md b/SESSION_STATE.md
new file mode 100644
index 0000000..db54207
--- /dev/null
+++ b/SESSION_STATE.md
@@ -0,0 +1,200 @@
+# SESSION STATE
+
+**Current Task Goal:** Add wind/current map overlay and weather forecast display that loads on application startup.
+
+**Status:** [IN_PROGRESS] — Planning complete, awaiting user approval to implement.
+
+---
+
+## Project Context
+
+- **Platform:** Android (Kotlin), API 24+, compileSdk 34
+- **Build system:** Gradle (Groovy DSL)
+- **Location:** `android-app/`
+- **Current state:** Empty project skeleton (no source code, only build files)
+- **Design doc:** `docs/COMPONENT_DESIGN.md`
+
+---
+
+## Completed Items
+
+- [x] Explored project structure
+- [x] Read COMPONENT_DESIGN.md — full architecture documented
+- [x] Identified UI framework: MapLibre GL Native (Android)
+- [x] Identified weather data sources (see below)
+- [x] Written implementation plan (this document)
+
+---
+
+## Data Sources
+
+| Data | Source | Auth |
+|------|--------|------|
+| Wind forecast (10m, kt) | Open-Meteo API (`api.open-meteo.com`) | None (free) |
+| Weather forecast (temp, precip, code) | Open-Meteo API | None (free) |
+| Marine / ocean currents | Open-Meteo Marine API (`marine-api.open-meteo.com`) | None (free) |
+| Base map tiles | MapLibre GL + OpenStreetMap tiles (free CDN) | None |
+
+Open-Meteo is chosen because it requires no API key, covers global marine/weather data, and returns JSON that's easy to parse. The design doc also lists NOAA and Windy as future sources; those can be swapped in later via the repository abstraction.
+
+### Key API endpoints
+
+**Weather forecast:**
+```
+GET https://api.open-meteo.com/v1/forecast
+ ?latitude={lat}&longitude={lon}
+ &hourly=windspeed_10m,winddirection_10m,temperature_2m,
+ precipitation_probability,weathercode
+ &forecast_days=7&wind_speed_unit=kn
+```
+
+**Marine/current:**
+```
+GET https://marine-api.open-meteo.com/v1/marine
+ ?latitude={lat}&longitude={lon}
+ &hourly=wave_height,wave_direction,ocean_current_velocity,
+ ocean_current_direction
+ &forecast_days=7
+```
+
+---
+
+## Architecture
+
+```
+Presentation
+ MainActivity (single-activity host)
+ ├── MapFragment — MapLibre map + wind-arrow overlay
+ └── ForecastFragment — 7-day forecast list
+
+ViewModel (lifecycle-aware state)
+ MainViewModel
+ ├── uiState: StateFlow<UiState> (Loading | Success | Error)
+ ├── windArrows: StateFlow<List<WindArrow>>
+ └── forecast: StateFlow<List<ForecastItem>>
+
+Repository
+ WeatherRepository
+ ├── fetchWeather(lat, lon): WeatherData
+ └── fetchMarine(lat, lon): MarineData
+
+Data / API
+ WeatherApiService (Retrofit — Open-Meteo weather endpoint)
+ MarineApiService (Retrofit — Open-Meteo marine endpoint)
+ ApiClient (OkHttp singleton, base URL config)
+
+Models
+ WeatherData, MarineData (API response data classes)
+ WindArrow (lat, lon, speed_kt, direction_deg)
+ ForecastItem (time, wind_kt, wind_dir, temp_c, precip_pct, icon)
+```
+
+**Startup flow:**
+1. MainActivity.onCreate → MainViewModel.init
+2. ViewModel launches coroutine → FusedLocationProviderClient.lastLocation
+3. Location → WeatherRepository.fetchWeather(lat, lon) and .fetchMarine(lat, lon) in parallel
+4. Results → StateFlow updates → UI observes:
+ - MapFragment draws wind arrows at sampled grid points (current hour)
+ - ForecastFragment renders RecyclerView with 7-day items
+
+---
+
+## Implementation Plan (TDD)
+
+### Step 1 — Project dependencies (`android-app/app/build.gradle`)
+Add:
+- `org.maplibre.gl:android-sdk:10.0.2`
+- `com.squareup.retrofit2:retrofit:2.9.0`
+- `com.squareup.retrofit2:converter-moshi:2.9.0`
+- `com.squareup.moshi:moshi-kotlin:1.15.0`
+- `com.squareup.okhttp3:okhttp:4.12.0`
+- `org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3`
+- `androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0`
+- `androidx.lifecycle:lifecycle-runtime-ktx:2.7.0`
+- `com.google.android.gms:play-services-location:21.2.0`
+- `androidx.fragment:fragment-ktx:1.6.2`
+- `androidx.recyclerview:recyclerview:1.3.2`
+- Test deps: `mockk:1.13.9`, `kotlinx-coroutines-test:1.7.3`, `turbine:1.1.0`
+
+### Step 2 — AndroidManifest.xml
+- INTERNET, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION permissions
+- Declare MainActivity as launcher activity
+
+### Step 3 — Models (`data/model/`)
+**Files:**
+- `WindArrow.kt` — `data class WindArrow(val lat: Double, val lon: Double, val speedKt: Double, val directionDeg: Double)`
+- `ForecastItem.kt` — hourly weather item
+- `WeatherResponse.kt`, `MarineResponse.kt` — Moshi-annotated API response classes
+
+**Tests (red first):**
+- `WindArrowTest.kt` — parse correctly, edge cases (0 kt, 360°)
+
+### Step 4 — API services (`data/api/`)
+**Files:**
+- `WeatherApiService.kt` — Retrofit interface with `@GET("v1/forecast")`
+- `MarineApiService.kt` — Retrofit interface with `@GET("v1/marine")`
+- `ApiClient.kt` — Retrofit singleton factory
+
+**Tests:**
+- `WeatherApiServiceTest.kt` — mock HTTP server (MockWebServer), verify request URL + parse response
+
+### Step 5 — Repository (`data/repository/WeatherRepository.kt`)
+- Coordinates parallel API calls via `async { }` + `awaitAll()`
+- Maps raw API responses → domain models (WindArrow, ForecastItem)
+- Returns `Result<T>` to propagate errors cleanly
+
+**Tests:**
+- `WeatherRepositoryTest.kt` — mock services, verify data mapping and error handling
+
+### Step 6 — ViewModel (`ui/MainViewModel.kt`)
+- Exposes `uiState: StateFlow<UiState>`, `windArrows`, `forecast`
+- On init: request location permission → fetch data
+
+**Tests:**
+- `MainViewModelTest.kt` — mock repository, test Loading/Success/Error states with Turbine
+
+### Step 7 — Layouts
+- `res/layout/activity_main.xml` — BottomNavigationView + FragmentContainerView
+- `res/layout/fragment_map.xml` — MapLibre MapView (fullscreen)
+- `res/layout/fragment_forecast.xml` — RecyclerView for forecast list
+- `res/layout/item_forecast.xml` — single forecast row
+
+### Step 8 — MainActivity + Fragments
+- `MainActivity.kt` — sets up bottom nav, fragment back-stack
+- `MapFragment.kt` — initialises MapLibre, adds wind-arrow symbol layer on map ready
+- `ForecastFragment.kt` — observes ViewModel.forecast, drives RecyclerView adapter
+
+### Step 9 — Wind Overlay on Map
+- Load wind data for current hour from `windArrows`
+- Sample to a sparse grid (~50 points visible on screen)
+- Add MapLibre `SymbolLayer` with rotated arrow icons
+- Arrow rotation = wind direction; size/opacity proportional to speed
+
+---
+
+## Next 3 Specific Steps (after approval)
+
+1. **`android-app/app/build.gradle`** — add all dependencies listed in Step 1; enable `buildFeatures { viewBinding true }`
+2. **`android-app/app/src/main/AndroidManifest.xml`** — create with INTERNET + LOCATION permissions
+3. **Write failing tests for `WindArrow` model** — `src/test/kotlin/.../data/model/WindArrowTest.kt`
+
+---
+
+## Constraints & Notes
+
+- No API keys needed (Open-Meteo is free/open).
+- MapLibre GL requires a style URL; use MapLibre's free demo style or OSM raster tiles.
+- Wind arrows will use a bundled SVG/PNG asset (arrow icon) for the symbol layer.
+- Location: request at runtime (Android 6+ permission model).
+- Min SDK 24 means no Jetpack Compose (needs API 21+, but project uses XML views).
+- All implementation follows TDD: write failing test → implement → green.
+
+---
+
+## Scripts Added
+
+_(none yet)_
+
+## Process Improvements
+
+_(none yet)_