Skip to content

CarPlay refactoring and bugfixing. #3219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions MapboxNavigation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@
8AAE94A126A60ADE00AA1127 /* CarPlayMapViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAE94A026A60ADE00AA1127 /* CarPlayMapViewControllerDelegate.swift */; };
8AB316C926BCA56D00C3AC76 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316C826BCA56D00C3AC76 /* UIViewController.swift */; };
8AB316CB26BCA72300C3AC76 /* CGSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316CA26BCA72300C3AC76 /* CGSize.swift */; };
8AB316A926BA026B00C3AC76 /* MapTemplateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316A826BA026B00C3AC76 /* MapTemplateProvider.swift */; };
8AB316AB26BA029100C3AC76 /* MapTemplateProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316AA26BA029100C3AC76 /* MapTemplateProviderDelegate.swift */; };
8ABB9E75268E0140009013A5 /* NavigationCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABB9E74268E0140009013A5 /* NavigationCameraTests.swift */; };
8ABCD6A426AA0D9400B121B9 /* route-for-navigation-camera-bearing-smoothing.json in Resources */ = {isa = PBXBuildFile; fileRef = 8ABCD6A326AA0D9400B121B9 /* route-for-navigation-camera-bearing-smoothing.json */; };
8AC3965325DC66570027A035 /* NavigationCameraType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC3965225DC66570027A035 /* NavigationCameraType.swift */; };
Expand Down Expand Up @@ -668,6 +670,8 @@
8AB316AE26BB315100C3AC76 /* CPMapTemplate+MBTestable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CPMapTemplate+MBTestable.m"; sourceTree = "<group>"; };
8AB316C826BCA56D00C3AC76 /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
8AB316CA26BCA72300C3AC76 /* CGSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSize.swift; sourceTree = "<group>"; };
8AB316A826BA026B00C3AC76 /* MapTemplateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTemplateProvider.swift; sourceTree = "<group>"; };
8AB316AA26BA029100C3AC76 /* MapTemplateProviderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTemplateProviderDelegate.swift; sourceTree = "<group>"; };
8ABB9E74268E0140009013A5 /* NavigationCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCameraTests.swift; sourceTree = "<group>"; };
8ABCD6A326AA0D9400B121B9 /* route-for-navigation-camera-bearing-smoothing.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-for-navigation-camera-bearing-smoothing.json"; sourceTree = "<group>"; };
8AC3965225DC66570027A035 /* NavigationCameraType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCameraType.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1069,6 +1073,8 @@
8AAE94A026A60ADE00AA1127 /* CarPlayMapViewControllerDelegate.swift */,
C5FFAC1420D96F5B009E7F98 /* CarPlayNavigationViewController.swift */,
8AEB28AB265FF42500EC7892 /* CarPlayNavigationViewControllerDelegate.swift */,
8AB316A826BA026B00C3AC76 /* MapTemplateProvider.swift */,
8AB316AA26BA029100C3AC76 /* MapTemplateProviderDelegate.swift */,
358E31D522562697009B3EC2 /* CarPlayCompassView.swift */,
352C35BF2134958F00D77796 /* RecentItem.swift */,
8A1943A82685DC680066E2F8 /* NavigationGeocodedPlacemark.swift */,
Expand Down Expand Up @@ -2389,7 +2395,9 @@
351BEC051E5BCC6C006FE110 /* LaneView.swift in Sources */,
C5A7EC5C1FD610A80008B9BA /* VisualInstructionComponent.swift in Sources */,
CFD47D9020FD85EC00BC1E49 /* ResourceOptionsManager.swift in Sources */,
8AB316AB26BA029100C3AC76 /* MapTemplateProviderDelegate.swift in Sources */,
B44177F82649B08400781319 /* UserLocationStyle.swift in Sources */,
8AB316A926BA026B00C3AC76 /* MapTemplateProvider.swift in Sources */,
351BEC0D1E5BCC72006FE110 /* Bundle.swift in Sources */,
8DF399B21FB257B30034904C /* UIGestureRecognizer.swift in Sources */,
DA8F3A7823B5DB7900B56786 /* SpeedLimitStyleKit.swift in Sources */,
Expand Down
145 changes: 67 additions & 78 deletions Sources/MapboxNavigation/CarPlayManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ public class CarPlayManager: NSObject {
*/
public var styles: [Style] {
didSet {
if let mapViewController = carPlayMapViewController {
mapViewController.styles = styles
}
carPlayMapViewController?.styles = styles
carPlayNavigationViewController?.styles = styles
}
}
Expand Down Expand Up @@ -182,7 +180,7 @@ public class CarPlayManager: NSObject {
private weak var navigationService: NavigationService?
private var idleTimerCancellable: IdleTimerManager.Cancellable?

internal var mapTemplateProvider: MapTemplateProvider
var mapTemplateProvider: MapTemplateProvider

/**
Initializes a new CarPlay manager that manages a connection to the CarPlay interface.
Expand All @@ -200,10 +198,10 @@ public class CarPlayManager: NSObject {
carPlayNavigationViewControllerClass: nil)
}

internal init(styles: [Style]? = nil,
directions: Directions? = nil,
eventsManager: NavigationEventsManager? = nil,
carPlayNavigationViewControllerClass: CarPlayNavigationViewController.Type? = nil) {
init(styles: [Style]? = nil,
directions: Directions? = nil,
eventsManager: NavigationEventsManager? = nil,
carPlayNavigationViewControllerClass: CarPlayNavigationViewController.Type? = nil) {
self.styles = styles ?? [DayStyle(), NightStyle()]
let mapboxDirections = directions ?? NavigationSettings.shared.directions
self.directions = mapboxDirections
Expand Down Expand Up @@ -331,11 +329,11 @@ extension CarPlayManager: CPApplicationDelegate {
}

func reloadButtons(for mapTemplate: CPMapTemplate) {
guard let mapViewController = carPlayMapViewController else {
guard let carPlayMapViewController = carPlayMapViewController else {
return
}

let traitCollection = mapViewController.traitCollection
let traitCollection = carPlayMapViewController.traitCollection

if let leadingButtons = delegate?.carPlayManager(self,
leadingNavigationBarButtonsCompatibleWith: traitCollection,
Expand All @@ -356,7 +354,7 @@ extension CarPlayManager: CPApplicationDelegate {
in: mapTemplate,
for: .browsing) {
mapTemplate.mapButtons = mapButtons
} else if let mapButtons = self.browsingMapButtons(for: mapTemplate) {
} else if let mapButtons = browsingMapButtons(for: mapTemplate) {
mapTemplate.mapButtons = mapButtons
}
}
Expand All @@ -369,7 +367,7 @@ extension CarPlayManager: CPApplicationDelegate {
in: mapTemplate,
for: .browsing) {
mapTemplate.mapButtons = mapButtons
} else if let mapButtons = self.browsingMapButtons(for: mapTemplate) {
} else if let mapButtons = browsingMapButtons(for: mapTemplate) {
mapTemplate.mapButtons = mapButtons
}

Expand All @@ -378,16 +376,17 @@ extension CarPlayManager: CPApplicationDelegate {
}

private func browsingMapButtons(for mapTemplate: CPMapTemplate) -> [CPMapButton]? {
guard let mapViewController = carPlayMapViewController else {
guard let carPlayMapViewController = carPlayMapViewController else {
return nil
}
var mapButtons = [
mapViewController.recenterButton,
mapViewController.zoomInButton,
mapViewController.zoomOutButton
carPlayMapViewController.recenterButton,
carPlayMapViewController.zoomInButton,
carPlayMapViewController.zoomOutButton
]
let panMapButton = mapViewController.panMapButton ?? mapViewController.panningInterfaceDisplayButton(for: mapTemplate)
mapViewController.panMapButton = panMapButton
let panMapButton = carPlayMapViewController.panMapButton ??
carPlayMapViewController.panningInterfaceDisplayButton(for: mapTemplate)
carPlayMapViewController.panMapButton = panMapButton
mapButtons.insert(panMapButton, at: 1)

return mapButtons
Expand All @@ -400,13 +399,17 @@ extension CarPlayManager: CPApplicationDelegate {
extension CarPlayManager: CPInterfaceControllerDelegate {

public func templateWillAppear(_ template: CPTemplate, animated: Bool) {
delegate?.carPlayManager(self, templateWillAppear: template, animated: animated)

if template == interfaceController?.rootTemplate,
let mapViewController = carPlayMapViewController {
mapViewController.recenterButton.isHidden = true
let carPlayMapViewController = carPlayMapViewController {
carPlayMapViewController.recenterButton.isHidden = true
}
}

public func templateDidAppear(_ template: CPTemplate, animated: Bool) {
delegate?.carPlayManager(self, templateDidAppear: template, animated: animated)

guard interfaceController?.topTemplate == mainMapTemplate,
template == interfaceController?.rootTemplate,
let carPlayMapViewController = carPlayMapViewController else { return }
Expand All @@ -417,21 +420,35 @@ extension CarPlayManager: CPInterfaceControllerDelegate {
}

public func templateWillDisappear(_ template: CPTemplate, animated: Bool) {
delegate?.carPlayManager(self, templateWillDisappear: template, animated: animated)

guard let interfaceController = interfaceController,
let topTemplate = interfaceController.topTemplate,
type(of: topTemplate) == CPSearchTemplate.self ||
interfaceController.templates.count == 1 else { return }

navigationMapView?.navigationCamera.follow()
}

public func templateDidDisappear(_ template: CPTemplate, animated: Bool) {
delegate?.carPlayManager(self, templateDidDisappear: template, animated: animated)
}
}

@available(iOS 12.0, *)
extension CarPlayManager {

/**
Calculates routes to the given destination using the [Mapbox Directions API](https://www.mapbox.com/api-documentation/navigation/#directions) and previews them on a map.

Upon successful calculation a new template will be pushed onto the template navigation hierarchy.

- parameter destination: A final destination `Waypoint`.
- parameter completionHandler: A closure to be executed when the calculation completes.
*/
public func previewRoutes(to destination: Waypoint, completionHandler: @escaping CompletionHandler) {
guard let rootViewController = carPlayMapViewController,
let userLocation = rootViewController.navigationMapView.mapView.location.latestLocation else {
guard let carPlayMapViewController = carPlayMapViewController,
let userLocation = carPlayMapViewController.navigationMapView.mapView.location.latestLocation else {
completionHandler()
return
}
Expand All @@ -451,29 +468,46 @@ extension CarPlayManager {
previewRoutes(between: [origin, destination], completionHandler: completionHandler)
}

/**
Allows to preview routes for a list of `Waypoint` objects.

- parameter waypoints: A list of `Waypoint` objects.
- parameter completionHandler: A closure to be executed when the calculation completes.
*/
public func previewRoutes(between waypoints: [Waypoint], completionHandler: @escaping CompletionHandler) {
let options = NavigationRouteOptions(waypoints: waypoints)
previewRoutes(for: options, completionHandler: completionHandler)
}

/**
Calculates routes satisfying the given options using the [Mapbox Directions API](https://www.mapbox.com/api-documentation/navigation/#directions) and previews them on a map.

- parameter routeOptions: A `RouteOptions` object, which specifies the criteria for results
returned by the Mapbox Directions API.
- parameter completionHandler: A closure to be executed when the calculation completes.
*/
public func previewRoutes(for options: RouteOptions, completionHandler: @escaping CompletionHandler) {
calculate(options) { [weak self] (session, result) in
guard let self = self else {
completionHandler()
return
}

self?.didCalculate(result,
in: session,
for: options,
completionHandler: completionHandler)
self.didCalculate(result,
in: session,
for: options,
completionHandler: completionHandler)
}
}

internal func calculate(_ options: RouteOptions, completionHandler: @escaping Directions.RouteCompletionHandler) {
func calculate(_ options: RouteOptions, completionHandler: @escaping Directions.RouteCompletionHandler) {
directions.calculateWithCache(options: options, completionHandler: completionHandler)
}

internal func didCalculate(_ result: Result<RouteResponse, DirectionsError>,
in session: Directions.Session,
for routeOptions: RouteOptions,
completionHandler: CompletionHandler) {
func didCalculate(_ result: Result<RouteResponse, DirectionsError>,
in session: Directions.Session,
for routeOptions: RouteOptions,
completionHandler: CompletionHandler) {
defer {
completionHandler()
}
Expand Down Expand Up @@ -826,6 +860,8 @@ extension CarPlayManager: CarPlayMapViewControllerDelegate {
}
}

// MARK: - MapTemplateProviderDelegate methods

@available(iOS 12.0, *)
extension CarPlayManager: MapTemplateProviderDelegate {

Expand Down Expand Up @@ -895,50 +931,3 @@ extension CarPlayManager {
idleTimerCancellable = nil
}
}

@available(iOS 12.0, *)
internal protocol MapTemplateProviderDelegate: AnyObject {

func mapTemplateProvider(_ provider: MapTemplateProvider,
mapTemplate: CPMapTemplate,
leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection,
for activity: CarPlayActivity) -> [CPBarButton]?

func mapTemplateProvider(_ provider: MapTemplateProvider,
mapTemplate: CPMapTemplate,
trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection,
for activity: CarPlayActivity) -> [CPBarButton]?
}

@available(iOS 12.0, *)
internal class MapTemplateProvider: NSObject {

weak var delegate: MapTemplateProviderDelegate?

func mapTemplate(forPreviewing trip: CPTrip,
traitCollection: UITraitCollection,
mapDelegate: CPMapTemplateDelegate) -> CPMapTemplate {
let mapTemplate = createMapTemplate()
mapTemplate.mapDelegate = mapDelegate

if let leadingButtons = delegate?.mapTemplateProvider(self,
mapTemplate: mapTemplate,
leadingNavigationBarButtonsCompatibleWith: traitCollection,
for: .previewing) {
mapTemplate.leadingNavigationBarButtons = leadingButtons
}

if let trailingButtons = delegate?.mapTemplateProvider(self,
mapTemplate: mapTemplate,
trailingNavigationBarButtonsCompatibleWith: traitCollection,
for: .previewing) {
mapTemplate.trailingNavigationBarButtons = trailingButtons
}

return mapTemplate
}

open func createMapTemplate() -> CPMapTemplate {
return CPMapTemplate()
}
}
64 changes: 64 additions & 0 deletions Sources/MapboxNavigation/CarPlayManagerDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,34 @@ public protocol CarPlayManagerDelegate: AnyObject, UnimplementedLogging {
didAdd finalDestinationAnnotation: PointAnnotation,
to parentViewController: UIViewController,
pointAnnotationManager: PointAnnotationManager)

/**
Called when a template presented by the `CarPlayManager` is about to appear on the screen.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateWillAppear template: CPTemplate,
animated: Bool)

/**
Called when a template presented by the `CarPlayManager` has finished appearing on the screen.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateDidAppear template: CPTemplate,
animated: Bool)

/**
Called when a template presented by the `CarPlayManager` is about to disappear from the screen.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateWillDisappear template: CPTemplate,
animated: Bool)

/**
Called when a template presented by the `CarPlayManager` has finished disappearing from the screen.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateDidDisappear template: CPTemplate,
animated: Bool)
}

@available(iOS 12.0, *)
Expand Down Expand Up @@ -273,4 +301,40 @@ public extension CarPlayManagerDelegate {
pointAnnotationManager: PointAnnotationManager) {
logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug)
}

/**
`UnimplementedLogging` prints a warning to standard output the first time this method is called.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateWillAppear template: CPTemplate,
animated: Bool) {
logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug)
}

/**
`UnimplementedLogging` prints a warning to standard output the first time this method is called.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateDidAppear template: CPTemplate,
animated: Bool) {
logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug)
}

/**
`UnimplementedLogging` prints a warning to standard output the first time this method is called.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateWillDisappear template: CPTemplate,
animated: Bool) {
logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug)
}

/**
`UnimplementedLogging` prints a warning to standard output the first time this method is called.
*/
func carPlayManager(_ carPlayManager: CarPlayManager,
templateDidDisappear template: CPTemplate,
animated: Bool) {
logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug)
}
}
Loading