@neoskola/auto-play 0.3.17 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
import CarPlay
|
|
9
9
|
|
|
10
|
+
private struct NowPlayingPushResult {
|
|
11
|
+
let success: Bool
|
|
12
|
+
let errorDescription: String?
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
@MainActor
|
|
11
16
|
class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate {
|
|
12
17
|
let interfaceController: CPInterfaceController
|
|
@@ -61,11 +66,28 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate {
|
|
|
61
66
|
return true
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
let
|
|
65
|
-
if
|
|
66
|
-
|
|
69
|
+
let firstAttempt = await pushNowPlayingTemplateSafely(animated: animated)
|
|
70
|
+
if firstAttempt.success || interfaceController.topTemplate is CPNowPlayingTemplate {
|
|
71
|
+
return true
|
|
67
72
|
}
|
|
68
|
-
|
|
73
|
+
|
|
74
|
+
// CarPlay can reject pushes while handling list selection transitions.
|
|
75
|
+
// Retry once after a short delay to avoid transient failures.
|
|
76
|
+
try? await Task.sleep(nanoseconds: 250_000_000)
|
|
77
|
+
let secondAttempt = await pushNowPlayingTemplateSafely(animated: animated)
|
|
78
|
+
if secondAttempt.success || interfaceController.topTemplate is CPNowPlayingTemplate {
|
|
79
|
+
return true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let reason = secondAttempt.errorDescription
|
|
83
|
+
?? firstAttempt.errorDescription
|
|
84
|
+
?? "unknown reason"
|
|
85
|
+
let topTemplateDescription = interfaceController.topTemplate.map {
|
|
86
|
+
String(describing: type(of: $0))
|
|
87
|
+
} ?? "nil"
|
|
88
|
+
throw AutoPlayError.pushFailed(
|
|
89
|
+
"CPNowPlayingTemplate push failed (\(reason)). top=\(topTemplateDescription) stackCount=\(interfaceController.templates.count)"
|
|
90
|
+
)
|
|
69
91
|
}
|
|
70
92
|
|
|
71
93
|
return try await interfaceController.pushTemplate(
|
|
@@ -76,7 +98,7 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate {
|
|
|
76
98
|
|
|
77
99
|
/// Pushes CPNowPlayingTemplate using ObjC @try/@catch to prevent crash from NSException.
|
|
78
100
|
/// Uses a completion handler to confirm the push actually succeeded.
|
|
79
|
-
private func pushNowPlayingTemplateSafely(animated: Bool) async ->
|
|
101
|
+
private func pushNowPlayingTemplateSafely(animated: Bool) async -> NowPlayingPushResult {
|
|
80
102
|
return await withCheckedContinuation { continuation in
|
|
81
103
|
do {
|
|
82
104
|
try ObjCExceptionCatcher.push(
|
|
@@ -88,12 +110,22 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate {
|
|
|
88
110
|
print("[AutoPlay] CPNowPlayingTemplate push completion error: \(error.localizedDescription)")
|
|
89
111
|
}
|
|
90
112
|
print("[AutoPlay] CPNowPlayingTemplate push completion: success=\(success)")
|
|
91
|
-
continuation.resume(
|
|
113
|
+
continuation.resume(
|
|
114
|
+
returning: NowPlayingPushResult(
|
|
115
|
+
success: success,
|
|
116
|
+
errorDescription: error?.localizedDescription
|
|
117
|
+
)
|
|
118
|
+
)
|
|
92
119
|
}
|
|
93
120
|
)
|
|
94
121
|
} catch {
|
|
95
122
|
print("[AutoPlay] CPNowPlayingTemplate push threw ObjC exception: \(error.localizedDescription)")
|
|
96
|
-
continuation.resume(
|
|
123
|
+
continuation.resume(
|
|
124
|
+
returning: NowPlayingPushResult(
|
|
125
|
+
success: false,
|
|
126
|
+
errorDescription: error.localizedDescription
|
|
127
|
+
)
|
|
128
|
+
)
|
|
97
129
|
}
|
|
98
130
|
}
|
|
99
131
|
}
|
|
@@ -307,10 +307,13 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate {
|
|
|
307
307
|
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
// Keep playback metadata populated even before duration is known.
|
|
311
|
+
// This helps CarPlay recognize an active now-playing session.
|
|
312
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
|
|
313
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
|
|
314
|
+
|
|
310
315
|
if currentDuration > 0 {
|
|
311
316
|
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentDuration
|
|
312
|
-
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
|
|
313
|
-
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
|
|
314
317
|
}
|
|
315
318
|
|
|
316
319
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|