0
0
Fork
You've already forked comaps
0
forked from comaps/comaps

testflight/2026.06.20 #14

Open
eisa01 wants to merge 32 commits from testflight/2026.06.20 into main
pull from: testflight/2026.06.20
merge into: eisa01:main
eisa01:main
eisa01:carplay-dashboard-support
eisa01:modes-ui
eisa01:eisa01/carplay-road-shields
eisa01:multi-surface-map-rendering
eisa01:carplay-0708
eisa01:forgejo-macos-runner
eisa01:create-venv
eisa01:carplay-0706
eisa01:carplay-0704-findings
eisa01:fable-review
eisa01:testflight/2026.06.30
eisa01:carplay-dashboard
eisa01:carplay-om-code-review
eisa01:carplay-code-review-jun28
eisa01:carplay-dashboard-mvp2
eisa01:car-follow-on-reacquire
eisa01:carplay-location-keepalive
eisa01:carplay-roundabout-exit-number
eisa01:carplay-destination
eisa01:carplay-dashboard-mvp
eisa01:name-cherry-picking
eisa01:om-pr-11243-2
eisa01:icu-cjk-fix
eisa01:simple-cjk-fix
eisa01:fix-crash-search
eisa01:scene-support
eisa01:om-carplay-button
eisa01:om-pr-10545
eisa01:submodule-main-branch
eisa01:improve-unit-testing-docs

Scheduled for next testflight

Scheduled for next testflight
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
Signed-off-by: eisa01 <eisa01@gmail.com>
Adds a CarPlay Dashboard scene showing a Search shortcut button and a
placeholder view (map rendering in the dashboard is a follow-up).
Requires the CPSupportsDashboardNavigationScene key and scene
configurations for both CarPlay roles in the scene manifest.
Also migrates the main CarPlay connection from the deprecated
CPApplicationDelegate to CPTemplateApplicationSceneDelegate, required
after UIScene adoption — the old callbacks no longer fire once a scene
manifest exists. Scene delegate classes use explicit @objc names since
the Swift module name varies between build configurations.
UIApplicationSupportsMultipleScenes stays false: CarPlay scenes connect
regardless, and enabling it would offer iPad multi-window, which the
single shared MapViewController doesn't support.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
With the scene-based lifecycle, the CarPlay scene can connect while no
phone window scene exists: when the app is force-quit on the phone with
CarPlay connected (iOS relaunches it immediately for CarPlay), or when
the app is launched directly into CarPlay.
In that state MapsAppDelegate.mapViewController hit its NSAssert, and
CarPlayService.setup passed the nil phone window to a non-optional
UIWindow parameter. Make the window and mapViewController properties
nullable, and defer attaching the map to the CarPlay window until the
phone scene connects, at which point SceneDelegate notifies
CarPlayService (attachMapIfNeeded).
Until the phone scene connects, CarPlay shows templates without the
map; decoupling the map view from the phone UI is a follow-up.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
The map stack (MWMNavigationController with MapViewController and the
single Drape surface) was only created when the phone scene
instantiated Main.storyboard, so in a CarPlay-only session (force-quit
relaunch, phone locked in the car) CarPlay showed templates with an
empty map area.
Make MapsAppDelegate own the map stack: it is created once on demand by
whichever scene connects first and reused for the app's lifetime. The
phone SceneDelegate now builds its window manually instead of via
UISceneStoryboardFile (this also fixes scene reconnection creating a
second MapViewController), CarPlayService creates the stack when
attaching the map with no phone scene present, and CarPlaySceneDelegate
drives the framework foreground/rendering lifecycle in car-only
sessions, where the phone SceneDelegate isn't there to do it.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
Host the shared map view in the CarPlay dashboard window whenever the
dashboard is visible, instead of a static placeholder. Per Apple's
guidelines the window contains only map content; the system draws ETA
cards and shortcut buttons itself.
CarPlayService now owns the decision of which window hosts the single
map view (updateMapHost): the visible dashboard wins, then the CarPlay
app window, then the phone — unless the user explicitly chose "Continue
on the phone". All scene lifecycle events funnel into this one
idempotent decision point, so activation-callback ordering between the
two car scenes doesn't matter. The dashboard can also be the first and
only connected scene (phone locked), in which case it creates the map
stack and the Drape engine itself.
The framework foreground/rendering lifecycle for car-only sessions
moved into a CarSceneLifecycle helper shared by both car scene
delegates: the app and dashboard scenes hand the car display over to
each other, so the framework only enters the background when neither
remains foreground-active.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
UIKit drives each scene's lifecycle independently, so the phone scene
resigning active on screen lock would disable rendering, stop frame
presentation and location updates, and background the framework even
while a CarPlay scene was showing the map.
Centralize the framework foreground/rendering lifecycle for all scenes
(phone, CarPlay app, CarPlay dashboard) in SceneLifecycle: the framework
enters the foreground with the first active scene and enters the
background only when no scene is foreground-active anymore. This also
fixes the frozen map when CarPlay connects while the phone is already
locked.
Also:
- Enable UIApplicationSupportsMultipleScenes per the CarPlay App
 Programming Guide; keep iPad single-window since the app has one
 shared map stack
- Defer the dashboard search button action until the CarPlay app
 scene has connected
- Let updateMapHost() handle the map re-parenting in
 switchScreenToPhone() instead of duplicating the transition
- Rename FrameworkHelper.setRenderingEnabled to enableRendering
Generated with Claude Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
The my-position arrow snapped off-center in the dashboard because
CarPlayMapViewController kept pushing visible viewports while the
dashboard hosted the map: its mapView is nil then, so template events
and its own layout passes sent the full car screen frame in points
(scale fallback 1) against a surface measured in phone-scale pixels,
putting the drape pivot in the top-left quarter of the surface. Only
push a viewport while actually hosting the map view, with its real
contentScaleFactor.
The visual scale adjustment also required a phone window as comparison
source, so car-first sessions (locked phone) kept rendering at the
phone's visual scale, ~50% oversized on a 2x car screen. The target
scale only depends on the destination window: rework the adjuster API
accordingly, track the last applied scale to skip redundant tile
recaches, and drop the deferred-adjustment state from CarPlayService.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
The car inherited whatever zoom level the phone was at (drape preserves
meters-per-pixel across host switches), which reads too close on a car
screen and especially in the small dashboard tile. Zoom out to level 15
once per CarPlay session, when the map first follows the position on a
car screen and no route is active, leaving navigation auto-zoom and
manual zooming alone.
Adds FrameworkHelper.setZoomLevel using relative scaling, which unlike
SetViewportCenter keeps the location follow mode intact.
Generated with Claude Code Fable 5
Signed-off-by: eisa01 <eisa01@gmail.com>
Signed-off-by: eisa01 <eisa01@gmail.com>
Surface turn lane recommendations during CarPlay navigation, as already provided by the routing core
Per Apple's CarPlay guidance, lanes are shown on two surfaces:
- Guidance panel: a full-width, symbol-only second CPManeuver whose symbolSet
 is a lane strip (recommended lane highlighted, others dimmed)
- Supported instrument clusters / HUDs: CPManeuver.linkedLaneGuidance
 (iOS 18+) (not tested, not possible in the normal simulator)
Generated with the help of Claude Opus 4.8
Signed-off-by: eisa01 <eisa01@gmail.com>
CarPlay previously rendered the upcoming turn as a single instruction
string built from the phone-oriented "[6A]: [US 101 South] > ..." text,
leaving the structured road data and the modern CPManeuver APIs unused.
Bridge the structured next-turn road components (name, ref, junction ref,
destination ref, destination, isLink) from FollowingInfo through to Swift,
alongside the maneuver direction, and use them to:
- Build CPManeuver.instructionVariants as a descending-length array so the
 head unit can pick the longest text that fits the available width,
 instead of a single pre-formatted string that just truncates.
- Populate the iOS 17.4 instrument-cluster / HUD metadata: maneuverType,
 junctionType, and highwayExitLabel.
The variants are composed from the structured fields (clean "Exit 6A:
US 101 South → San Jose" rather than the bracketed phone syntax), falling
back to the existing formatted street name. The new FollowingInfo field is
additive and unused on Android, but should be tested
Generated with the help ofClaude Opus 4.8
Signed-off-by: eisa01 <eisa01@gmail.com>
Surface turn lane recommendations during in-app phone navigation, as already
provided by the routing core and already shown on Android and CarPlay.
The routing core exposes lanes via FollowingInfo::m_lanes, but the phone
navigation dashboard ignored it. This reuses the LaneWay / MWMLaneInfo Swift
models introduced for CarPlay:
- LaneWay gains turnImageName, mapping each direction to the existing
 NavigationDashboard/Turn/* arrow assets (mirrors the Android LaneWay.mTurnRes
 mapping and the existing CarPlay symbolName).
- MWMNavigationDashboardEntity gains a lanes property, populated from m_lanes
 for car routing via the same GetActiveLaneWays() extraction used by
 MWMRoutingManager.
- NavigationLanesView draws the upcoming turn's lanes as a horizontal strip:
 the recommended lane(s) at full opacity, the rest dimmed, hidden when empty.
- MWMNavigationInfoView places the strip beside the turn box, mirroring Android.
Generated with the help of Claude Opus 4.8
Signed-off-by: eisa01 <eisa01@gmail.com>
Sync with Carplay, move to shared helper
Signed-off-by: eisa01 <eisa01@gmail.com>
Mirrors iOS + Android
Signed-off-by: eisa01 <eisa01@gmail.com>
eisa01 force-pushed testflight/2026.06.20 from ecca960915 to 7744eb0a5d 2026年06月20日 16:15:52 +02:00 Compare
When CoMaps was launched directly into CarPlay, and the last session on the phone was not the following mode, CarPlay did not have the position in focus either
Engage following automatically once the location settles on a car screen (CarPlay app or dashboard), so the map centers and follows on startup.
Guarded to once per session so it never re-forces following after the user intentionally stops it.
Generated with the help of Claude Opus 4.8
Signed-off-by: eisa01 <eisa01@gmail.com>
This pull request has changes conflicting with the target branch.
  • data/styles/outdoors/dark/style.mapcss
  • data/styles/outdoors/include/priorities_1_BG-by-size.prio.txt
  • data/styles/outdoors/light/style.mapcss
  • data/styles/vehicle/dark/style.mapcss
  • data/styles/vehicle/light/style.mapcss
  • data/styles/walking/dark/symbols/planet-m.svg
  • data/styles/walking/dark/symbols/planet-s.svg
  • data/styles/walking/dark/symbols/shield-brazil-ac-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ac.svg
  • data/styles/walking/dark/symbols/shield-brazil-al-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-al.svg
  • data/styles/walking/dark/symbols/shield-brazil-am-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-am.svg
  • data/styles/walking/dark/symbols/shield-brazil-ap-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ap.svg
  • data/styles/walking/dark/symbols/shield-brazil-ba-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ba.svg
  • data/styles/walking/dark/symbols/shield-brazil-ce-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ce.svg
  • data/styles/walking/dark/symbols/shield-brazil-df-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-df.svg
  • data/styles/walking/dark/symbols/shield-brazil-es-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-es.svg
  • data/styles/walking/dark/symbols/shield-brazil-go-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-go.svg
  • data/styles/walking/dark/symbols/shield-brazil-ma-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ma.svg
  • data/styles/walking/dark/symbols/shield-brazil-mg-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-mg.svg
  • data/styles/walking/dark/symbols/shield-brazil-ms-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ms.svg
  • data/styles/walking/dark/symbols/shield-brazil-mt-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-mt.svg
  • data/styles/walking/dark/symbols/shield-brazil-national-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-national.svg
  • data/styles/walking/dark/symbols/shield-brazil-pa-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-pa.svg
  • data/styles/walking/dark/symbols/shield-brazil-pb-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-pb.svg
  • data/styles/walking/dark/symbols/shield-brazil-pe-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-pe.svg
  • data/styles/walking/dark/symbols/shield-brazil-pi-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-pi.svg
  • data/styles/walking/dark/symbols/shield-brazil-pr-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-pr.svg
  • data/styles/walking/dark/symbols/shield-brazil-rj-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-rj.svg
  • data/styles/walking/dark/symbols/shield-brazil-rn-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-rn.svg
  • data/styles/walking/dark/symbols/shield-brazil-ro-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-ro.svg
  • data/styles/walking/dark/symbols/shield-brazil-rr-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-rr.svg
  • data/styles/walking/dark/symbols/shield-brazil-rs-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-rs.svg
  • data/styles/walking/dark/symbols/shield-brazil-sc-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-sc.svg
  • data/styles/walking/dark/symbols/shield-brazil-se-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-se.svg
  • data/styles/walking/dark/symbols/shield-brazil-sp-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-sp.svg
  • data/styles/walking/dark/symbols/shield-brazil-to-scaled.svg
  • data/styles/walking/dark/symbols/shield-brazil-to.svg
  • data/styles/walking/dark/symbols/toilets_private-m.svg
  • data/styles/walking/light/symbols/planet-m.svg
  • data/styles/walking/light/symbols/planet-s.svg
  • data/styles/walking/light/symbols/shield-brazil-ac-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ac.svg
  • data/styles/walking/light/symbols/shield-brazil-al-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-al.svg
  • data/styles/walking/light/symbols/shield-brazil-am-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-am.svg
  • data/styles/walking/light/symbols/shield-brazil-ap-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ap.svg
  • data/styles/walking/light/symbols/shield-brazil-ba-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ba.svg
  • data/styles/walking/light/symbols/shield-brazil-ce-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ce.svg
  • data/styles/walking/light/symbols/shield-brazil-df-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-df.svg
  • data/styles/walking/light/symbols/shield-brazil-es-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-es.svg
  • data/styles/walking/light/symbols/shield-brazil-go-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-go.svg
  • data/styles/walking/light/symbols/shield-brazil-ma-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ma.svg
  • data/styles/walking/light/symbols/shield-brazil-mg-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-mg.svg
  • data/styles/walking/light/symbols/shield-brazil-ms-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ms.svg
  • data/styles/walking/light/symbols/shield-brazil-mt-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-mt.svg
  • data/styles/walking/light/symbols/shield-brazil-national-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-national.svg
  • data/styles/walking/light/symbols/shield-brazil-pa-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-pa.svg
  • data/styles/walking/light/symbols/shield-brazil-pb-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-pb.svg
  • data/styles/walking/light/symbols/shield-brazil-pe-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-pe.svg
  • data/styles/walking/light/symbols/shield-brazil-pi-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-pi.svg
  • data/styles/walking/light/symbols/shield-brazil-pr-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-pr.svg
  • data/styles/walking/light/symbols/shield-brazil-rj-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-rj.svg
  • data/styles/walking/light/symbols/shield-brazil-rn-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-rn.svg
  • data/styles/walking/light/symbols/shield-brazil-ro-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-ro.svg
  • data/styles/walking/light/symbols/shield-brazil-rr-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-rr.svg
  • data/styles/walking/light/symbols/shield-brazil-rs-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-rs.svg
  • data/styles/walking/light/symbols/shield-brazil-sc-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-sc.svg
  • data/styles/walking/light/symbols/shield-brazil-se-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-se.svg
  • data/styles/walking/light/symbols/shield-brazil-sp-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-sp.svg
  • data/styles/walking/light/symbols/shield-brazil-to-scaled.svg
  • data/styles/walking/light/symbols/shield-brazil-to.svg
  • data/styles/walking/light/symbols/toilets_private-m.svg
  • data/styles/walking_outdoor/include/priorities_3_FG.prio.txt
  • data/styles/walking_outdoor/include/priorities_4_overlays.prio.txt
  • iphone/Maps/Classes/CarPlay/CarPlayRouter.swift
  • iphone/Maps/Classes/CarPlay/Templates Data/RouteInfo.swift
  • iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager+Entity.mm
  • iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm
  • iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/NavigationLanesView.swift
  • iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/MWMiPhoneRoutePreview.xib
  • iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/RoutePreviewStatus/BaseRoutePreviewStatus.swift
  • iphone/Maps/Core/Framework/ProxyObjects/Routing/MWMRoutingManager.mm
  • iphone/Maps/Maps.xcodeproj/project.pbxproj
  • iphone/Maps/Tests/Classes/CarPlay/CarPlayServiceTests.swift
  • libs/map/routing_manager.cpp
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin testflight/2026.06.20:testflight/2026.06.20
git switch testflight/2026.06.20

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff testflight/2026.06.20
git switch testflight/2026.06.20
git rebase main
git switch main
git merge --ff-only testflight/2026.06.20
git switch testflight/2026.06.20
git rebase main
git switch main
git merge --no-ff testflight/2026.06.20
git switch main
git merge --squash testflight/2026.06.20
git switch main
git merge --ff-only testflight/2026.06.20
git switch main
git merge testflight/2026.06.20
git push origin main
Sign in to join this conversation.
No reviewers
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
eisa01/comaps!14
Reference in a new issue
eisa01/comaps
No description provided.
Delete branch "testflight/2026.06.20"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?