SkySight clouds on Android
Summary
Section titled “Summary”iOS lays SkySight’s satellite cloud images over the flight map and swaps them as the replay
moves, so a pilot sees the clouds they flew under (Satellite clouds and radar).
Android has no SkySight at all: ProFeature.SKY_SIGHT exists but is never shown.
This analysis brings only the cloud overlay to Android. The SkySight forecasts (layer picker,
regions, catalogue, legends, day strip, wind, task and in-flight maps) stay out
(SkySight forecasts remains not-planned on Android).
What Android needs:
- A SkySight account in Connected services (sign in, sign out), reduced to what the clouds use.
- The frame and tile rules: Satellite cloud frames.
- A tile download with a disk cache, using SkySight satellite tiles.
- A cloud button on the flight map, and raster layers that follow the replay.
- The settings around it: auto-download, cache size and clear.
- The Pro gate and the paywall entry for SkySight.
The satellite tiles need no SkySight key. The sign-in is still required, because Android does the same as iOS (decision 3).
Behaviour
Section titled “Behaviour”Entry points
Section titled “Entry points”| Where | What |
|---|---|
| Flight map, top bar | The cloud button. iOS: satelliteBarButtonItem, accessibility label accessibility.button.satellite. |
| Flight map, overflow menu | “Clouds” (menu.button.clouds) with an on/off check, when the bar has no room for the button. iOS puts it there on a compact phone that shows all three inspector tabs. Android decides from its own top bar width. |
| Flight map, hardware keyboard | iOS has the shortcut toggleClouds. Android adds it only if FlightMapShortcuts already mirrors the iOS shortcut list. |
| Settings › Connected services › SkySight | Sign in, auto-download, cache, sign out. |
| Settings › Advanced | “Clear cloud overlay cache” with its size. |
The cloud button
Section titled “The cloud button”The button has four looks. The arrow tells the pilot that a tap downloads first.
| State | Glyph (iOS SF Symbol) | Tap does |
|---|---|---|
| Tiles not all on disk | icloud.and.arrow.down | downloads, then switches the overlay on |
| Tiles on disk, overlay off | cloud | switches it on |
| Overlay on | cloud.fill (lit) | switches it off |
| Downloading or preparing | cloud inside a progress ring | nothing (button disabled) |
- The button is disabled when no flight is loaded, when no flight on the map has imagery (archive rules), or during a download.
- Switching on checks, in this order:
- Pro (
skySight). Without it the paywall opens for SkySight and nothing else happens. - A SkySight account. Without it the SkySight sign-in opens; after a successful sign-in the tap carries on (download or show) without a second tap.
- Pro (
- Switching off never asks for Pro or an account.
- Progress: the ring fills with
downloaded / totaltiles, counting tiles already on disk as done. After the download the ring stays at 99 % until the first frame is drawn on the map, with a 3-second backstop so the ring never stays up. - A failed download (any tile not
200, offline, timeout) ends silently: the ring goes away and the button shows the download glyph again. Tiles that did arrive stay in the cache, so a retry only fetches the missing ones. iOS shows no error message.
The overlay
Section titled “The overlay”- One raster layer per frame, drawn below the flight tracks and above the base map, at a fixed opacity of 0.5 (the forecast opacity setting does not apply).
- When switched on, it shows the frame at the selected flight’s midpoint. From then on it follows the replay time, scrub or playback, of the selected flight (Which frame is shown).
- Scrubbing must feel instant. iOS keeps the two frames on each side of the shown one loaded, and when it jumps to a frame that is not loaded it keeps the previous frame on screen until the new one’s tiles are in (map idle, or 1 s at most). The pilot always sees a whole frame, never half a frame.
- Tiles are read from the download cache, never from the network, once downloaded. A tile the map fetches on its own (zoom 7, or a frame it asks for before the download is done) is stored in the same cache.
- Switching the overlay off removes the layers and frees their memory.
- Changing the flights on the map (add, compare, remove) switches the overlay off and works out imagery and “downloaded” again for the new set.
- A map style change keeps the overlay on, at the same frame.
- Leaving the flight map does not keep the overlay. It is off when a flight map opens, unless the automatic download below switches it on.
Automatic download
Section titled “Automatic download”When a flight map opens, the tiles download by themselves and the overlay switches on when they are in, if all of these are true:
- Pro
skySightis unlocked (no paywall is shown for an automatic download), - “Auto-download satellite data” is on (default off),
- a SkySight account is signed in,
- a flight on the map has imagery,
- not all of its tiles are on disk yet.
When the tiles are already on disk, the automatic path does nothing: the overlay stays off until the pilot taps.
SkySight account (Settings › Connected services)
Section titled “SkySight account (Settings › Connected services)”Connected services gets a Weather section with one row, SkySight (settings.service.skysight),
after the flight services, as on iOS. The SkySight screen holds only what the clouds use:
| Section | Rows | Strings |
|---|---|---|
| Your credentials | Username, Password; Save in the top bar | settings.skysight.labels.header, …username, …password |
| (footer) | Where to create an account | settings.skysight.labels.footer |
| Satellite | “Auto-download satellite data” toggle; “Clear satellite cache” with the size, hidden size when empty | settings.skysight.labels.autodownload, …clearcache, footer …autodownload.footer |
| “Log out from SkySight” (only when signed in) | settings.skysight.labels.clear |
Left out on Android, because they belong to the forecasts: the opacity slider
(map.settings.skysight.labels.opacity.*) and “Remember current weather” (settings.skysight.labels.persist).
- Save calls the sign in. On success it stores
username and password and closes the screen. On failure it shows
skysight.error.labels.invalidand stores nothing. The fields and Save are disabled while the request runs. - Signed in means a password is stored (iOS
isAuthenticated = password != nil). - Log out removes username and password and closes the screen. It does not clear the tile
cache (iOS
clearData). - Connecting is free. Using the clouds needs Pro.
- The Connected services row on the Settings root lists SkySight when signed in, as it does for the other services.
- Location: the app’s cache directory,
satellite/{yyyyMMdd-HHmm}/{z}/{x}/{y}.jpg(frame time in UTC). It is a cache: the OS may evict it, nothing is backed up or synced, and evicted tiles download again. - Size: the total of the files, shown in both settings rows.
- Clear deletes the folder. From Settings › Advanced it asks first
(
settings.label.cache.satellite.confirm.title/…confirm.message). From the SkySight screen it clears at once and the size updates. These are the iOS behaviours. - After a clear, any open flight map cancels a running download, switches the overlay off and
shows the download glyph again. iOS only does this for the SkySight screen’s button
(
satelliteCacheClearedNotification). Android does it for both. - No size limit and no expiry, as on iOS.
Edge cases
Section titled “Edge cases”| Case | Behaviour |
|---|---|
| Flight before 2014, or before 2023 outside Europe/Africa | No imagery: left out. If no flight on the map has imagery, the button is disabled. |
| Several flights, some without imagery | Only the flights with imagery are downloaded and count for “all on disk”. |
| Several flights on different days | Frames per flight, merged; no frames for the days in between. |
| Flight crosses midnight UTC | Times are the flight date plus seconds, past 24:00 included (quirk). |
| Offline with tiles on disk | Works: the overlay is drawn from the cache. |
| Offline without tiles | The download fails silently (see above). |
| Pro lapses while the overlay is on | Nothing changes until the next tap that switches it on. |
| SkySight password changed or account cancelled | The tiles still load (they need no key). The app keeps the account until the pilot logs out (decision 5). |
- Preference (already in Android
LocalSettings):skySightAutoDownloadSatellite(VultureSkySightAutoDownloadSatellite), Bool, defaultfalse, device-local, not synced. The iOS preference import already maps it. - Credentials: SkySight username and password, encrypted on the device, never synced or backed
up, like the other services (decision P7-D3). Android adds
skySightUsernameandskySightPasswordtoServiceCredentials. The session key is not stored, because the clouds do not use it. - Tile cache: files only, see Cache. No database field. The early design’s
isSatelliteCached/satelliteTimestampson the flight was never built on iOS. “Downloaded” is worked out from the files each time. - No sync schema change, no file format, no deep link.
- SkySight satellite tiles:
POST https://skysight.io/api/authandGET https://satellite.skysight.io/tiles/{z}/{x}/{y}@2x?date=…&mtg=true. - Nothing in vulture-web.
/api/skysight(the forecast catalogue) is not needed.
Strings
Section titled “Strings”All user-facing strings exist on iOS and are already in Android’s generated strings_ios.xml:
| iOS key | English |
|---|---|
menu.button.clouds | Clouds |
accessibility.button.satellite | Toggle weather overlay |
settings.service.skysight | SkySight |
settings.skysight.labels.title | SkySight |
settings.skysight.labels.header | Your credentials |
settings.skysight.labels.username / …password | Username / Password |
settings.skysight.labels.footer | Create your account at https://skysight.io and enjoy weather overlays and satellite cloud imagery during flight analysis in Vulture. |
settings.skysight.labels.autodownload | Auto-download satellite data |
settings.skysight.labels.autodownload.footer | Automatically download the satellite imagery for a flight when you open it. … |
settings.skysight.labels.clearcache | Clear satellite cache |
settings.skysight.labels.clear | Log out from SkySight |
skysight.error.labels.invalid | The given credentials are incorrect. |
settings.label.cache.satellite | Clear cloud overlay cache |
settings.label.cache.satellite.confirm.title / …message | Clear the cloud overlay cache? / SkySight’s satellite clouds are downloaded again when a flight shows them. |
subscription.benefit.skysight.title / …detail | SkySight weather / Forecasts and satellite clouds on the map. |
subscription.feature.skysight.title / …subtitle | SkySight weather on the map / Lay SkySight’s forecasts and the satellite clouds over your map, … |
The footer and the two paywall strings mention forecasts, which Android will not have. The
sign-in footer stays. The paywall gets Android-only strings in :feature:paywall’s
strings.xml, in all 8 locales (decision 4):
| Android key | English |
|---|---|
paywall_benefit_skysight_clouds_detail | Satellite clouds on the map. |
paywall_feature_skysight_clouds_subtitle | See the clouds you flew under on the flight map, with your own SkySight account. |
The titles (subscription.benefit.skysight.title, subscription.feature.skysight.title) are reused.
Decisions
Section titled “Decisions”| # | Question | Decision | Date |
|---|---|---|---|
| 1 | How much SkySight comes to Android? | Only the satellite cloud overlay on the flight map, with the sign-in, settings and paywall entry it needs. No forecasts. | 2026-10-19 |
| 2 | Auth for the tiles? | None needed: the tile answers 200 without key or Origin (checked 2026-10-19). The session key is not stored or refreshed on Android. | 2026-10-19 |
| 3 | Require a SkySight account, although the tiles are open? | Yes. Pro and a signed-in SkySight account, the same as iOS. iOS keeps its gate. | 2026-10-19 |
| 4 | Paywall copy on Android? | Android-only benefit detail and feature subtitle that mention only the clouds. The titles and the sign-in footer stay as they are. | 2026-10-19 |
| 5 | Check the account again after sign-in? | No. Android checks the account only when the pilot signs in. A changed or cancelled account keeps working for the clouds until the pilot logs out. | 2026-10-19 |
| 6 | Paywall hero image for SKY_SIGHT? | Capture the Android flight map with the clouds on (as in decision P8-D7), light and dark. | 2026-10-19 |
Open questions
Section titled “Open questions”- Midnight UTC on iOS: iOS builds times with
Calendar.date(bySettingHour:…), which looks wrong past 24:00 UTC. Verify on iOS and, if confirmed, add it to iOS changes. This doesn’t block Android, which follows the domain rule.
Platforms
Section titled “Platforms”Reference implementation, nothing to build. Read:
| What | Path (vulture-ios) |
|---|---|
| Tile math, frames, availability | Packages/Flights/Sources/Flights/Satellite/SatelliteTileManager+Tiles.swift, +Availability.swift |
| Download (4 at a time, 15 s request / 60 s resource timeouts) | Packages/Flights/Sources/Flights/Satellite/SatelliteTileManager.swift |
| Cache paths, size, clear | Packages/Flights/Sources/Flights/Satellite/SatelliteTileManager+Cache.swift |
| Gates, intents, midpoint, auto-download | Modules/App/Sources/Map/Flight/FlightMapViewModel.swift (// MARK: - Satellite) |
| Button, ring, menu item, sign-in hand-off | Modules/App/Sources/Map/Flight/FlightMapViewController.swift (toggleSatelliteOverlay, satelliteBarButtonItem, dataMenu) |
| Layers, warm window, swap on idle | Modules/App/Sources/Map/Flight/DataSource/FlightMapViewDataSource+Satellite.swift, +Delegate.swift (style reload) |
Cache-backed tile requests, Origin header | Modules/App/Sources/Map/SkySight/SkySightHttpInterceptor.swift |
| Sign-in | Modules/App/Sources/Services/SkySight/SkySightManager.swift, Packages/SkySight/Sources/SkySight/Requests/Authenticate/ |
| SkySight settings screen | Packages/SettingsScreens/Sources/SettingsScreens/Services/SkySight/SkySightAuthView.swift, …ViewModel.swift, Modules/App/Sources/Features/Settings/Settings/Infrastructure/AppSkySightAuth.swift |
| Advanced cache row | Packages/SettingsScreens/Sources/SettingsScreens/Settings/Main/MainSettingsViewModel+Sections.swift, MainSettingsModels.swift |
Layer constants that make scrubbing instant (worth copying): warm radius 2 frames, warm opacity
0.01, shown 0.5, hidden 0, raster-fade-duration 0, cold-swap timeout 1 s.
Android
Section titled “Android”| Area | Existing code (vulture-android) | Change |
|---|---|---|
| Pro | data/billing/…/ProFeature.kt (SKY_SIGHT) | Use it. feature/paywall/…/ProBenefit.kt: drop SKY_SIGHT from the excluded set. PaywallContext.kt: the new hero (decision 6) and the Android-only copy (decision 4). |
| Credentials | data/credentials/…/ServiceCredentials.kt, CredentialStore.kt, ConnectedServices.kt | Add SkySight username and password (hidden in toString). signedOut is keyed on DiscoverService, which SkySight is not: add a SkySight sign-out next to it. |
| Sign-in client | core/network (HttpClientFactory) | POST /api/auth with X-API-KEY: Vulture. Keep it small, for example data/skysight. |
| Services UI | feature/services/…/ServicesScreen.kt, ServicesViewModel.kt, SignInAccountScreens.kt, SignInAccountViewModels.kt | Weather section and a SkySightAccountViewModel on SignInAccountViewModel, plus the satellite section. Update the KDoc that says “not in Android v1”. |
| Settings root | feature/settings/…/root/SettingsRootUiState.kt (ConnectedService) | Add SkySight. |
| Advanced settings | feature/settings/…/advanced/AdvancedSettingsSection.kt | Add “Clear cloud overlay cache” with its size. The footer note about the missing row goes. |
| Prefs | data/prefs/…/LocalSettings.kt (skySightAutoDownloadSatellite) | Already there. Wire it to the toggle. |
| Frame and tile rules | none | Pure Kotlin in a JVM module (for example domain/flightmap, or a new domain/satellite), unit-tested against the domain page. |
| Download and cache | data/elevation is the nearest pattern | Downloader (4 parallel, timeouts as iOS, all-or-fail result, progress), cache in context.cacheDir/satellite/, size and clear, with a flow so open maps hear about a clear. |
| Map | core/map/…/flightmap/FlightMapController.kt, MapboxFlightMapController.kt, FlightMapLayerIds.kt | New controller calls, for example setSatelliteFrames(frames) and showSatelliteFrame(time?). Raster sources and layers below the first flight layer, the warm window, swap on map idle, restore after a style change. Fake in tests. |
| Cached tiles | Mapbox Android v11.32 (gradle/libs.versions.toml) | Answer satellite.skysight.io requests from the cache and store the ones the map fetches, through the Mapbox common HTTP interceptor (HttpServiceFactory), registered once in app. Check the exact API in 11.32. |
| Flight map | feature/flightmap/…/topbar/FlightMapTopBar.kt (“Not ported: the SkySight clouds button”), topbar/OverflowMenu.kt, FlightMapViewModel.kt, FlightMapUiState.kt (time: ReplayTime) | Button with its four looks and ring, menu item, gates (paywall, sign-in then continue), auto-download on open, follow ReplayTime of the selected flight, reset when the flights change. |
| Changelog | CHANGELOG.md | “Added: SkySight satellite clouds on the flight map.” |
Android caveats:
- The replay clock (
ReplayTime) is already seconds since 00:00 UTC of the flight date and keeps counting past midnight, which is the time the domain page asks for. - With alignment (compare), use the selected flight’s own time, not the aligned clock.
- Don’t use
String.formatorSimpleDateFormatwith the default locale for thedatequery or the cache folder. Use a UTCDateTimeFormatterwithLocale.ROOT.
No changes.
- vulture-android: frame and tile rules (JVM) with unit tests: frames, tile range, padding, archive limits, nearest frame, midpoint, past-midnight time.
- vulture-android: SkySight credentials, sign-in client, Connected services Weather section and SkySight screen, Settings root row.
- vulture-android: tile downloader and cache (size, clear, clear notification), auto-download preference wired.
- vulture-android: Mapbox HTTP interceptor (cache-backed satellite tiles), raster frame layers in
:core:mapbehindFlightMapController. - vulture-android: flight map cloud button, overflow item, gates, ring, auto-download, replay following.
- vulture-android: Advanced “Clear cloud overlay cache” row; paywall benefit, Android-only copy (8 locales); changelog.
- vulture-android: paywall hero capture for
SKY_SIGHT(decision 6; needs a device with a SkySight account, steps infeature/paywall/README.md, “Hero images”). Until then the welcome image shows. - vulture-ios: (optional) verify the midnight-UTC quirk (open question).
- vulture-docs: set Android
doneon Satellite clouds and radar and Connected services, andstatus: shippedhere.
