functional carplay experience in simulators
This commit is contained in:
@@ -16,9 +16,44 @@ import UIKit
|
||||
*/
|
||||
class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPListTemplateDelegate {
|
||||
|
||||
private struct Snapshot {
|
||||
var playbackStatus: String
|
||||
var title: String
|
||||
var subtitle: String
|
||||
var album: String
|
||||
|
||||
init(playbackStatus: String, title: String, subtitle: String, album: String) {
|
||||
self.playbackStatus = playbackStatus
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.album = album
|
||||
}
|
||||
|
||||
static let `default` = Snapshot(
|
||||
playbackStatus: "stopped",
|
||||
title: "KRYZ Live Stream",
|
||||
subtitle: "KRYZ Radio",
|
||||
album: "Live"
|
||||
)
|
||||
|
||||
init(payload: [String: Any]?) {
|
||||
let base = Snapshot.default
|
||||
let status = (payload?["playbackStatus"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let title = (payload?["title"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let subtitle = (payload?["subtitle"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let album = (payload?["album"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
playbackStatus = (status?.isEmpty == false) ? status! : base.playbackStatus
|
||||
self.title = (title?.isEmpty == false) ? title! : base.title
|
||||
self.subtitle = (subtitle?.isEmpty == false) ? subtitle! : base.subtitle
|
||||
self.album = (album?.isEmpty == false) ? album! : base.album
|
||||
}
|
||||
}
|
||||
|
||||
private var carPlayInterfaceController: CPInterfaceController?
|
||||
private var listTemplate: CPListTemplate?
|
||||
private var streamItem: CPListItem?
|
||||
private var snapshot: Snapshot = .default
|
||||
|
||||
// MARK: - CPTemplateApplicationSceneDelegate
|
||||
|
||||
@@ -26,7 +61,10 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPL
|
||||
_ templateApplicationScene: CPTemplateApplicationScene,
|
||||
didConnect interfaceController: CPInterfaceController
|
||||
) {
|
||||
appDelegate?.ensureFlutterBridgeReadyForCarPlay()
|
||||
carPlayInterfaceController = interfaceController
|
||||
snapshot = Snapshot(payload: appDelegate?.currentCarPlaySnapshot())
|
||||
|
||||
let rootTemplate = makeListTemplate()
|
||||
if #available(iOS 14.0, *) {
|
||||
interfaceController.setRootTemplate(rootTemplate, animated: false, completion: nil)
|
||||
@@ -34,12 +72,25 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPL
|
||||
// iOS 13 fallback: use API variant available before iOS 14.
|
||||
interfaceController.setRootTemplate(rootTemplate, animated: false)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(handleCarPlaySnapshotChanged(_:)),
|
||||
name: AppDelegate.carPlaySnapshotDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
appDelegate?.requestCarPlaySnapshot()
|
||||
}
|
||||
|
||||
func templateApplicationScene(
|
||||
_ templateApplicationScene: CPTemplateApplicationScene,
|
||||
didDisconnectInterfaceController interfaceController: CPInterfaceController
|
||||
) {
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: AppDelegate.carPlaySnapshotDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
carPlayInterfaceController = nil
|
||||
listTemplate = nil
|
||||
appDelegate?.invokeStop()
|
||||
@@ -52,8 +103,8 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPL
|
||||
.withRenderingMode(.alwaysOriginal)
|
||||
|
||||
let item = CPListItem(
|
||||
text: "KRYZ Live Stream",
|
||||
detailText: "KRYZ Radio",
|
||||
text: snapshot.title,
|
||||
detailText: detailText(for: snapshot),
|
||||
image: icon
|
||||
)
|
||||
streamItem = item
|
||||
@@ -82,10 +133,57 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate, CPL
|
||||
return template
|
||||
}
|
||||
|
||||
private func detailText(for snapshot: Snapshot) -> String {
|
||||
let statusText: String = switch snapshot.playbackStatus {
|
||||
case "playing":
|
||||
"Playing"
|
||||
case "buffering":
|
||||
"Buffering"
|
||||
default:
|
||||
"Stopped"
|
||||
}
|
||||
|
||||
if snapshot.subtitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
return statusText
|
||||
}
|
||||
|
||||
return "\(snapshot.subtitle) • \(statusText)"
|
||||
}
|
||||
|
||||
@objc private func handleCarPlaySnapshotChanged(_ notification: Notification) {
|
||||
guard
|
||||
let payload = notification.userInfo?[AppDelegate.carPlaySnapshotUserInfoKey] as? [String: Any]
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
snapshot = Snapshot(payload: payload)
|
||||
refreshTemplatesForSnapshot()
|
||||
}
|
||||
|
||||
private func refreshTemplatesForSnapshot() {
|
||||
guard let interfaceController = carPlayInterfaceController else {
|
||||
return
|
||||
}
|
||||
|
||||
if #available(iOS 14.0, *), interfaceController.topTemplate is CPNowPlayingTemplate {
|
||||
// Preserve Now Playing navigation stack while metadata updates are flowing.
|
||||
return
|
||||
}
|
||||
|
||||
let updatedTemplate = makeListTemplate()
|
||||
if #available(iOS 14.0, *) {
|
||||
interfaceController.setRootTemplate(updatedTemplate, animated: false, completion: nil)
|
||||
} else {
|
||||
interfaceController.setRootTemplate(updatedTemplate, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Playback
|
||||
|
||||
private func handleStreamItemTapped() {
|
||||
appDelegate?.invokePlay()
|
||||
appDelegate?.requestCarPlaySnapshot()
|
||||
guard let interfaceController = carPlayInterfaceController else {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user