Skip to content

SkySight clouds on Android

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:

  1. A SkySight account in Connected services (sign in, sign out), reduced to what the clouds use.
  2. The frame and tile rules: Satellite cloud frames.
  3. A tile download with a disk cache, using SkySight satellite tiles.
  4. A cloud button on the flight map, and raster layers that follow the replay.
  5. The settings around it: auto-download, cache size and clear.
  6. 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).

WhereWhat
Flight map, top barThe 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 keyboardiOS has the shortcut toggleClouds. Android adds it only if FlightMapShortcuts already mirrors the iOS shortcut list.
Settings › Connected services › SkySightSign in, auto-download, cache, sign out.
Settings › Advanced“Clear cloud overlay cache” with its size.

The button has four looks. The arrow tells the pilot that a tap downloads first.

StateGlyph (iOS SF Symbol)Tap does
Tiles not all on diskicloud.and.arrow.downdownloads, then switches the overlay on
Tiles on disk, overlay offcloudswitches it on
Overlay oncloud.fill (lit)switches it off
Downloading or preparingcloud inside a progress ringnothing (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:
    1. Pro (skySight). Without it the paywall opens for SkySight and nothing else happens.
    2. 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.
  • Switching off never asks for Pro or an account.
  • Progress: the ring fills with downloaded / total tiles, 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.
  • 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.

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 skySight is 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:

SectionRowsStrings
Your credentialsUsername, Password; Save in the top barsettings.skysight.labels.header, …username, …password
(footer)Where to create an accountsettings.skysight.labels.footer
Satellite“Auto-download satellite data” toggle; “Clear satellite cache” with the size, hidden size when emptysettings.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.invalid and 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.
CaseBehaviour
Flight before 2014, or before 2023 outside Europe/AfricaNo imagery: left out. If no flight on the map has imagery, the button is disabled.
Several flights, some without imageryOnly the flights with imagery are downloaded and count for “all on disk”.
Several flights on different daysFrames per flight, merged; no frames for the days in between.
Flight crosses midnight UTCTimes are the flight date plus seconds, past 24:00 included (quirk).
Offline with tiles on diskWorks: the overlay is drawn from the cache.
Offline without tilesThe download fails silently (see above).
Pro lapses while the overlay is onNothing changes until the next tap that switches it on.
SkySight password changed or account cancelledThe 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, default false, 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 skySightUsername and skySightPassword to ServiceCredentials. 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/satelliteTimestamps on 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/auth and GET https://satellite.skysight.io/tiles/{z}/{x}/{y}@2x?date=…&mtg=true.
  • Nothing in vulture-web. /api/skysight (the forecast catalogue) is not needed.

All user-facing strings exist on iOS and are already in Android’s generated strings_ios.xml:

iOS keyEnglish
menu.button.cloudsClouds
accessibility.button.satelliteToggle weather overlay
settings.service.skysightSkySight
settings.skysight.labels.titleSkySight
settings.skysight.labels.headerYour credentials
settings.skysight.labels.username / …passwordUsername / Password
settings.skysight.labels.footerCreate your account at https://skysight.io and enjoy weather overlays and satellite cloud imagery during flight analysis in Vulture.
settings.skysight.labels.autodownloadAuto-download satellite data
settings.skysight.labels.autodownload.footerAutomatically download the satellite imagery for a flight when you open it. …
settings.skysight.labels.clearcacheClear satellite cache
settings.skysight.labels.clearLog out from SkySight
skysight.error.labels.invalidThe given credentials are incorrect.
settings.label.cache.satelliteClear cloud overlay cache
settings.label.cache.satellite.confirm.title / …messageClear the cloud overlay cache? / SkySight’s satellite clouds are downloaded again when a flight shows them.
subscription.benefit.skysight.title / …detailSkySight weather / Forecasts and satellite clouds on the map.
subscription.feature.skysight.title / …subtitleSkySight 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 keyEnglish
paywall_benefit_skysight_clouds_detailSatellite clouds on the map.
paywall_feature_skysight_clouds_subtitleSee 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.

#QuestionDecisionDate
1How 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
2Auth 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
3Require 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
4Paywall 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
5Check 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
6Paywall 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
  • 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.

Reference implementation, nothing to build. Read:

WhatPath (vulture-ios)
Tile math, frames, availabilityPackages/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, clearPackages/Flights/Sources/Flights/Satellite/SatelliteTileManager+Cache.swift
Gates, intents, midpoint, auto-downloadModules/App/Sources/Map/Flight/FlightMapViewModel.swift (// MARK: - Satellite)
Button, ring, menu item, sign-in hand-offModules/App/Sources/Map/Flight/FlightMapViewController.swift (toggleSatelliteOverlay, satelliteBarButtonItem, dataMenu)
Layers, warm window, swap on idleModules/App/Sources/Map/Flight/DataSource/FlightMapViewDataSource+Satellite.swift, +Delegate.swift (style reload)
Cache-backed tile requests, Origin headerModules/App/Sources/Map/SkySight/SkySightHttpInterceptor.swift
Sign-inModules/App/Sources/Services/SkySight/SkySightManager.swift, Packages/SkySight/Sources/SkySight/Requests/Authenticate/
SkySight settings screenPackages/SettingsScreens/Sources/SettingsScreens/Services/SkySight/SkySightAuthView.swift, …ViewModel.swift, Modules/App/Sources/Features/Settings/Settings/Infrastructure/AppSkySightAuth.swift
Advanced cache rowPackages/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.

AreaExisting code (vulture-android)Change
Prodata/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).
Credentialsdata/credentials/…/ServiceCredentials.kt, CredentialStore.kt, ConnectedServices.ktAdd 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 clientcore/network (HttpClientFactory)POST /api/auth with X-API-KEY: Vulture. Keep it small, for example data/skysight.
Services UIfeature/services/…/ServicesScreen.kt, ServicesViewModel.kt, SignInAccountScreens.kt, SignInAccountViewModels.ktWeather section and a SkySightAccountViewModel on SignInAccountViewModel, plus the satellite section. Update the KDoc that says “not in Android v1”.
Settings rootfeature/settings/…/root/SettingsRootUiState.kt (ConnectedService)Add SkySight.
Advanced settingsfeature/settings/…/advanced/AdvancedSettingsSection.ktAdd “Clear cloud overlay cache” with its size. The footer note about the missing row goes.
Prefsdata/prefs/…/LocalSettings.kt (skySightAutoDownloadSatellite)Already there. Wire it to the toggle.
Frame and tile rulesnonePure Kotlin in a JVM module (for example domain/flightmap, or a new domain/satellite), unit-tested against the domain page.
Download and cachedata/elevation is the nearest patternDownloader (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.
Mapcore/map/…/flightmap/FlightMapController.kt, MapboxFlightMapController.kt, FlightMapLayerIds.ktNew 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 tilesMapbox 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 mapfeature/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.
ChangelogCHANGELOG.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.format or SimpleDateFormat with the default locale for the date query or the cache folder. Use a UTC DateTimeFormatter with Locale.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:map behind FlightMapController.
  • 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 in feature/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 done on Satellite clouds and radar and Connected services, and status: shipped here.