@neoskola/auto-play 0.3.4 → 0.3.6
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.
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/NowPlayingTemplate.kt +15 -5
- package/ios/templates/NowPlayingTemplate.swift +221 -55
- package/lib/templates/NowPlayingTemplate.d.ts +2 -0
- package/nitrogen/generated/android/c++/JNowPlayingTemplateConfig.hpp +25 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NowPlayingTemplateConfig.kt +10 -4
- package/nitrogen/generated/ios/swift/NowPlayingTemplateConfig.swift +83 -1
- package/nitrogen/generated/shared/c++/NowPlayingTemplateConfig.hpp +9 -1
- package/package.json +1 -1
- package/src/templates/NowPlayingTemplate.ts +2 -0
|
@@ -33,7 +33,17 @@ class NowPlayingTemplate(context: CarContext, config: NowPlayingTemplateConfig)
|
|
|
33
33
|
.build()
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// Onceki Bolum butonu
|
|
37
|
+
config.onPreviousTrack?.let { prevTrack ->
|
|
38
|
+
paneBuilder.addAction(
|
|
39
|
+
Action.Builder()
|
|
40
|
+
.setTitle(CarText.create("Önceki"))
|
|
41
|
+
.setOnClickListener { prevTrack() }
|
|
42
|
+
.build()
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Play/Pause butonu
|
|
37
47
|
val playPauseTitle = if (config.isPlaying) "Duraklat" else "Oynat"
|
|
38
48
|
paneBuilder.addAction(
|
|
39
49
|
Action.Builder()
|
|
@@ -48,12 +58,12 @@ class NowPlayingTemplate(context: CarContext, config: NowPlayingTemplateConfig)
|
|
|
48
58
|
.build()
|
|
49
59
|
)
|
|
50
60
|
|
|
51
|
-
//
|
|
52
|
-
config.
|
|
61
|
+
// Sonraki Bolum butonu
|
|
62
|
+
config.onNextTrack?.let { nextTrack ->
|
|
53
63
|
paneBuilder.addAction(
|
|
54
64
|
Action.Builder()
|
|
55
|
-
.setTitle(CarText.create("
|
|
56
|
-
.setOnClickListener {
|
|
65
|
+
.setTitle(CarText.create("Sonraki"))
|
|
66
|
+
.setOnClickListener { nextTrack() }
|
|
57
67
|
.build()
|
|
58
68
|
)
|
|
59
69
|
}
|
|
@@ -3,7 +3,7 @@ import MediaPlayer
|
|
|
3
3
|
import AVFoundation
|
|
4
4
|
|
|
5
5
|
class NowPlayingTemplate: AutoPlayTemplate {
|
|
6
|
-
var template:
|
|
6
|
+
var template: CPListTemplate
|
|
7
7
|
var config: NowPlayingTemplateConfig
|
|
8
8
|
private var loadedImage: UIImage?
|
|
9
9
|
private var isSetupComplete = false
|
|
@@ -34,19 +34,79 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
34
34
|
init(config: NowPlayingTemplateConfig) {
|
|
35
35
|
self.config = config
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
let titleText = Parser.parseText(text: config.title) ?? "Now Playing"
|
|
38
|
+
let subtitleText = config.subtitle.flatMap { Parser.parseText(text: $0) } ?? ""
|
|
39
|
+
|
|
40
|
+
// Section 1: Bilgi — ders ve kurs adi
|
|
41
|
+
let infoItem = CPListItem(
|
|
42
|
+
text: titleText,
|
|
43
|
+
detailText: subtitleText,
|
|
44
|
+
image: UIImage(systemName: "music.note"),
|
|
45
|
+
accessoryImage: nil,
|
|
46
|
+
accessoryType: .none
|
|
47
|
+
)
|
|
48
|
+
let infoSection = CPListSection(
|
|
49
|
+
items: [infoItem],
|
|
50
|
+
header: "Şimdi Oynatılıyor",
|
|
51
|
+
sectionIndexTitle: nil
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// Section 2: Sure bilgisi
|
|
55
|
+
let timeItem = CPListItem(
|
|
56
|
+
text: "Yükleniyor...",
|
|
57
|
+
detailText: nil,
|
|
58
|
+
image: UIImage(systemName: "clock"),
|
|
59
|
+
accessoryImage: nil,
|
|
60
|
+
accessoryType: .none
|
|
61
|
+
)
|
|
62
|
+
let timeSection = CPListSection(
|
|
63
|
+
items: [timeItem],
|
|
64
|
+
header: nil,
|
|
65
|
+
sectionIndexTitle: nil
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// Section 3: Kontroller — onceki, oynat/duraklat, sonraki
|
|
69
|
+
let prevItem = CPListItem(
|
|
70
|
+
text: "Önceki Bölüm",
|
|
71
|
+
detailText: nil,
|
|
72
|
+
image: UIImage(systemName: "backward.end.fill"),
|
|
73
|
+
accessoryImage: nil,
|
|
74
|
+
accessoryType: .none
|
|
75
|
+
)
|
|
76
|
+
let playPauseItem = CPListItem(
|
|
77
|
+
text: "Oynat",
|
|
78
|
+
detailText: nil,
|
|
79
|
+
image: UIImage(systemName: "play.circle.fill"),
|
|
80
|
+
accessoryImage: nil,
|
|
81
|
+
accessoryType: .none
|
|
82
|
+
)
|
|
83
|
+
let nextItem = CPListItem(
|
|
84
|
+
text: "Sonraki Bölüm",
|
|
85
|
+
detailText: nil,
|
|
86
|
+
image: UIImage(systemName: "forward.end.fill"),
|
|
87
|
+
accessoryImage: nil,
|
|
88
|
+
accessoryType: .none
|
|
89
|
+
)
|
|
90
|
+
let controlSection = CPListSection(
|
|
91
|
+
items: [prevItem, playPauseItem, nextItem],
|
|
92
|
+
header: nil,
|
|
93
|
+
sectionIndexTitle: nil
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
template = CPListTemplate(
|
|
97
|
+
title: "Now Playing",
|
|
98
|
+
sections: [infoSection, timeSection, controlSection],
|
|
99
|
+
assistantCellConfiguration: nil,
|
|
100
|
+
id: config.id
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
// Handler'lari ayarla
|
|
104
|
+
setupListItemHandlers(prevItem: prevItem, playPauseItem: playPauseItem, nextItem: nextItem)
|
|
39
105
|
|
|
40
|
-
// Constructor runs on the JS thread. Dispatch all CarPlay UI setup to main thread.
|
|
41
|
-
// CPNowPlayingTemplate.shared is Apple's singleton — must be modified on main thread.
|
|
42
106
|
DispatchQueue.main.async { [weak self] in
|
|
43
107
|
guard let self = self else { return }
|
|
44
|
-
|
|
45
|
-
// Activate AVAudioSession FIRST — iOS needs this to recognize the app
|
|
46
|
-
// as a media player before MPNowPlayingInfoCenter metadata is meaningful.
|
|
47
108
|
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
48
|
-
|
|
49
|
-
self.setupNowPlayingButtons()
|
|
109
|
+
self.setupRemoteCommandCenter()
|
|
50
110
|
self.updateNowPlayingInfo()
|
|
51
111
|
self.isSetupComplete = true
|
|
52
112
|
|
|
@@ -56,6 +116,122 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
56
116
|
}
|
|
57
117
|
}
|
|
58
118
|
|
|
119
|
+
// MARK: - List Item Handlers
|
|
120
|
+
|
|
121
|
+
private func setupListItemHandlers(prevItem: CPListItem, playPauseItem: CPListItem, nextItem: CPListItem) {
|
|
122
|
+
prevItem.handler = { [weak self] _, completion in
|
|
123
|
+
self?.config.onPreviousTrack?()
|
|
124
|
+
completion()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
playPauseItem.handler = { [weak self] _, completion in
|
|
128
|
+
guard let self = self else { completion(); return }
|
|
129
|
+
if self.config.isPlaying {
|
|
130
|
+
self.pauseAudio()
|
|
131
|
+
self.config.onPause?()
|
|
132
|
+
} else {
|
|
133
|
+
self.resumeAudio()
|
|
134
|
+
self.config.onPlay?()
|
|
135
|
+
}
|
|
136
|
+
completion()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
nextItem.handler = { [weak self] _, completion in
|
|
140
|
+
self?.config.onNextTrack?()
|
|
141
|
+
completion()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// MARK: - Player UI
|
|
146
|
+
|
|
147
|
+
private func updatePlayerUI() {
|
|
148
|
+
let titleText = Parser.parseText(text: config.title) ?? "Now Playing"
|
|
149
|
+
let subtitleText = config.subtitle.flatMap { Parser.parseText(text: $0) } ?? ""
|
|
150
|
+
|
|
151
|
+
// Section 1: Bilgi
|
|
152
|
+
let infoItem = CPListItem(
|
|
153
|
+
text: titleText,
|
|
154
|
+
detailText: subtitleText,
|
|
155
|
+
image: loadedImage ?? UIImage(systemName: "music.note"),
|
|
156
|
+
accessoryImage: nil,
|
|
157
|
+
accessoryType: .none
|
|
158
|
+
)
|
|
159
|
+
let infoSection = CPListSection(
|
|
160
|
+
items: [infoItem],
|
|
161
|
+
header: "Şimdi Oynatılıyor",
|
|
162
|
+
sectionIndexTitle: nil
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
// Section 2: Sure
|
|
166
|
+
let elapsed = formatTime(currentElapsedTime)
|
|
167
|
+
let total = currentDuration > 0 ? formatTime(currentDuration) : "--:--"
|
|
168
|
+
let timeText: String
|
|
169
|
+
let timeIcon: String
|
|
170
|
+
if config.isPlaying {
|
|
171
|
+
timeText = "\(elapsed) / \(total)"
|
|
172
|
+
timeIcon = "waveform"
|
|
173
|
+
} else {
|
|
174
|
+
timeText = "Duraklatıldı \(elapsed) / \(total)"
|
|
175
|
+
timeIcon = "pause.circle"
|
|
176
|
+
}
|
|
177
|
+
let timeItem = CPListItem(
|
|
178
|
+
text: timeText,
|
|
179
|
+
detailText: nil,
|
|
180
|
+
image: UIImage(systemName: timeIcon),
|
|
181
|
+
accessoryImage: nil,
|
|
182
|
+
accessoryType: .none
|
|
183
|
+
)
|
|
184
|
+
let timeSection = CPListSection(
|
|
185
|
+
items: [timeItem],
|
|
186
|
+
header: nil,
|
|
187
|
+
sectionIndexTitle: nil
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
// Section 3: Kontroller
|
|
191
|
+
let prevItem = CPListItem(
|
|
192
|
+
text: "Önceki Bölüm",
|
|
193
|
+
detailText: nil,
|
|
194
|
+
image: UIImage(systemName: "backward.end.fill"),
|
|
195
|
+
accessoryImage: nil,
|
|
196
|
+
accessoryType: .none
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
let playPauseText = config.isPlaying ? "Duraklat" : "Oynat"
|
|
200
|
+
let playPauseIcon = config.isPlaying ? "pause.circle.fill" : "play.circle.fill"
|
|
201
|
+
let playPauseItem = CPListItem(
|
|
202
|
+
text: playPauseText,
|
|
203
|
+
detailText: nil,
|
|
204
|
+
image: UIImage(systemName: playPauseIcon),
|
|
205
|
+
accessoryImage: nil,
|
|
206
|
+
accessoryType: .none
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
let nextItem = CPListItem(
|
|
210
|
+
text: "Sonraki Bölüm",
|
|
211
|
+
detailText: nil,
|
|
212
|
+
image: UIImage(systemName: "forward.end.fill"),
|
|
213
|
+
accessoryImage: nil,
|
|
214
|
+
accessoryType: .none
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
setupListItemHandlers(prevItem: prevItem, playPauseItem: playPauseItem, nextItem: nextItem)
|
|
218
|
+
|
|
219
|
+
let controlSection = CPListSection(
|
|
220
|
+
items: [prevItem, playPauseItem, nextItem],
|
|
221
|
+
header: nil,
|
|
222
|
+
sectionIndexTitle: nil
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
template.updateSections([infoSection, timeSection, controlSection])
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private func formatTime(_ seconds: Double) -> String {
|
|
229
|
+
guard !seconds.isNaN && !seconds.isInfinite && seconds >= 0 else { return "0:00" }
|
|
230
|
+
let mins = Int(seconds) / 60
|
|
231
|
+
let secs = Int(seconds) % 60
|
|
232
|
+
return String(format: "%d:%02d", mins, secs)
|
|
233
|
+
}
|
|
234
|
+
|
|
59
235
|
// MARK: - Native Audio Playback
|
|
60
236
|
|
|
61
237
|
@MainActor
|
|
@@ -73,12 +249,11 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
73
249
|
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
74
250
|
config.isPlaying = true
|
|
75
251
|
updateNowPlayingInfo()
|
|
252
|
+
updatePlayerUI()
|
|
76
253
|
MPNowPlayingInfoCenter.default().playbackState = .playing
|
|
77
254
|
|
|
78
255
|
print("[NowPlayingTemplate] Downloading audio: \(url)")
|
|
79
256
|
|
|
80
|
-
// Download file first, then play from local — R2/Cloudflare CDN streaming
|
|
81
|
-
// causes FigFilePlayer errors with AVPlayer, local playback is reliable
|
|
82
257
|
downloadTask = URLSession.shared.downloadTask(with: audioURL) { [weak self] tempURL, response, error in
|
|
83
258
|
guard let self = self else { return }
|
|
84
259
|
|
|
@@ -92,7 +267,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
92
267
|
return
|
|
93
268
|
}
|
|
94
269
|
|
|
95
|
-
// Move to a persistent temp location (URLSession deletes the file after this block)
|
|
96
270
|
let localURL = FileManager.default.temporaryDirectory
|
|
97
271
|
.appendingPathComponent("carplay_audio_\(UUID().uuidString).mp3")
|
|
98
272
|
|
|
@@ -121,7 +295,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
121
295
|
let asset = AVURLAsset(url: localURL)
|
|
122
296
|
playerItem = AVPlayerItem(asset: asset)
|
|
123
297
|
|
|
124
|
-
// KVO: detect duration as soon as asset loads (critical for progress bar)
|
|
125
298
|
statusObservation = playerItem?.observe(\.status, options: [.new]) { [weak self] item, _ in
|
|
126
299
|
guard item.status == .readyToPlay else {
|
|
127
300
|
if item.status == .failed {
|
|
@@ -135,6 +308,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
135
308
|
if !duration.isNaN && !duration.isInfinite && duration > 0 {
|
|
136
309
|
self.currentDuration = duration
|
|
137
310
|
self.updateNowPlayingInfo()
|
|
311
|
+
self.updatePlayerUI()
|
|
138
312
|
print("[NowPlayingTemplate] Duration resolved via KVO: \(duration)s")
|
|
139
313
|
}
|
|
140
314
|
}
|
|
@@ -147,7 +321,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
147
321
|
player?.seek(to: time)
|
|
148
322
|
}
|
|
149
323
|
|
|
150
|
-
// Periodic time observer (every 1 second) for MPNowPlayingInfoCenter updates
|
|
151
324
|
let interval = CMTime(seconds: 1.0, preferredTimescale: 600)
|
|
152
325
|
timeObserver = player?.addPeriodicTimeObserver(
|
|
153
326
|
forInterval: interval,
|
|
@@ -156,7 +329,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
156
329
|
self?.handleTimeUpdate(time: time)
|
|
157
330
|
}
|
|
158
331
|
|
|
159
|
-
// Playback finished notification
|
|
160
332
|
didFinishObserver = NotificationCenter.default.addObserver(
|
|
161
333
|
forName: .AVPlayerItemDidPlayToEndTime,
|
|
162
334
|
object: playerItem,
|
|
@@ -165,7 +337,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
165
337
|
self?.handlePlaybackFinished()
|
|
166
338
|
}
|
|
167
339
|
|
|
168
|
-
// Progress report timer (every 30 seconds) — calls JS callback for backend reporting
|
|
169
340
|
progressReportTimer = Timer.scheduledTimer(
|
|
170
341
|
withTimeInterval: 30.0,
|
|
171
342
|
repeats: true
|
|
@@ -190,10 +361,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
190
361
|
currentDuration = duration
|
|
191
362
|
}
|
|
192
363
|
|
|
193
|
-
// Use existing info or create fresh if nil (race condition safety)
|
|
194
364
|
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
|
|
195
365
|
|
|
196
|
-
// Ensure title/artist are present if this is a fresh dictionary
|
|
197
366
|
if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
|
|
198
367
|
let titleText = Parser.parseText(text: config.title) ?? ""
|
|
199
368
|
nowPlayingInfo[MPMediaItemPropertyTitle] = titleText
|
|
@@ -209,6 +378,9 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
209
378
|
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
|
|
210
379
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|
|
211
380
|
|
|
381
|
+
// Update custom player UI
|
|
382
|
+
updatePlayerUI()
|
|
383
|
+
|
|
212
384
|
// 95% completion check
|
|
213
385
|
if !completionFired && currentDuration > 0 && currentTime / currentDuration >= 0.95 {
|
|
214
386
|
completionFired = true
|
|
@@ -226,7 +398,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
226
398
|
private func handlePlaybackFinished() {
|
|
227
399
|
config.isPlaying = false
|
|
228
400
|
MPNowPlayingInfoCenter.default().playbackState = .stopped
|
|
229
|
-
|
|
401
|
+
updatePlayerUI()
|
|
230
402
|
if !completionFired {
|
|
231
403
|
completionFired = true
|
|
232
404
|
config.onComplete?()
|
|
@@ -239,6 +411,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
239
411
|
player?.pause()
|
|
240
412
|
config.isPlaying = false
|
|
241
413
|
updatePlaybackState(isPlaying: false)
|
|
414
|
+
updatePlayerUI()
|
|
242
415
|
reportProgress()
|
|
243
416
|
}
|
|
244
417
|
|
|
@@ -248,6 +421,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
248
421
|
player?.play()
|
|
249
422
|
config.isPlaying = true
|
|
250
423
|
updatePlaybackState(isPlaying: true)
|
|
424
|
+
updatePlayerUI()
|
|
251
425
|
}
|
|
252
426
|
|
|
253
427
|
@MainActor
|
|
@@ -272,6 +446,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
272
446
|
cleanupPlayer()
|
|
273
447
|
config.isPlaying = false
|
|
274
448
|
MPNowPlayingInfoCenter.default().playbackState = .stopped
|
|
449
|
+
updatePlayerUI()
|
|
275
450
|
}
|
|
276
451
|
|
|
277
452
|
private func cleanupPlayer() {
|
|
@@ -298,29 +473,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
298
473
|
}
|
|
299
474
|
}
|
|
300
475
|
|
|
301
|
-
// MARK: -
|
|
302
|
-
|
|
303
|
-
private func setupNowPlayingButtons() {
|
|
304
|
-
var buttons: [CPNowPlayingButton] = []
|
|
305
|
-
|
|
306
|
-
let skipBackButton = CPNowPlayingImageButton(
|
|
307
|
-
image: UIImage(systemName: "gobackward.30")!
|
|
308
|
-
) { [weak self] _ in
|
|
309
|
-
self?.seekBackward(seconds: 30)
|
|
310
|
-
self?.config.onSkipBackward?()
|
|
311
|
-
}
|
|
312
|
-
buttons.append(skipBackButton)
|
|
313
|
-
|
|
314
|
-
let skipForwardButton = CPNowPlayingImageButton(
|
|
315
|
-
image: UIImage(systemName: "goforward.30")!
|
|
316
|
-
) { [weak self] _ in
|
|
317
|
-
self?.seekForward(seconds: 30)
|
|
318
|
-
self?.config.onSkipForward?()
|
|
319
|
-
}
|
|
320
|
-
buttons.append(skipForwardButton)
|
|
321
|
-
|
|
322
|
-
template.updateNowPlayingButtons(buttons)
|
|
323
|
-
}
|
|
476
|
+
// MARK: - Now Playing Info & Remote Commands
|
|
324
477
|
|
|
325
478
|
private func updateNowPlayingInfo() {
|
|
326
479
|
let titleText = Parser.parseText(text: config.title) ?? ""
|
|
@@ -339,7 +492,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
339
492
|
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
|
|
340
493
|
}
|
|
341
494
|
|
|
342
|
-
// Include duration and elapsed time for the progress bar
|
|
343
495
|
if currentDuration > 0 {
|
|
344
496
|
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentDuration
|
|
345
497
|
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
|
|
@@ -347,8 +499,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
347
499
|
}
|
|
348
500
|
|
|
349
501
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|
|
350
|
-
|
|
351
|
-
setupRemoteCommandCenter()
|
|
352
502
|
}
|
|
353
503
|
|
|
354
504
|
private func setupRemoteCommandCenter() {
|
|
@@ -388,6 +538,22 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
388
538
|
return .success
|
|
389
539
|
}
|
|
390
540
|
|
|
541
|
+
// Sonraki bolum
|
|
542
|
+
commandCenter.nextTrackCommand.isEnabled = true
|
|
543
|
+
commandCenter.nextTrackCommand.removeTarget(nil)
|
|
544
|
+
commandCenter.nextTrackCommand.addTarget { [weak self] _ in
|
|
545
|
+
self?.config.onNextTrack?()
|
|
546
|
+
return .success
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Onceki bolum
|
|
550
|
+
commandCenter.previousTrackCommand.isEnabled = true
|
|
551
|
+
commandCenter.previousTrackCommand.removeTarget(nil)
|
|
552
|
+
commandCenter.previousTrackCommand.addTarget { [weak self] _ in
|
|
553
|
+
self?.config.onPreviousTrack?()
|
|
554
|
+
return .success
|
|
555
|
+
}
|
|
556
|
+
|
|
391
557
|
commandCenter.changePlaybackPositionCommand.isEnabled = true
|
|
392
558
|
commandCenter.changePlaybackPositionCommand.removeTarget(nil)
|
|
393
559
|
commandCenter.changePlaybackPositionCommand.addTarget { [weak self] event in
|
|
@@ -399,6 +565,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
399
565
|
self.player?.seek(to: time)
|
|
400
566
|
self.currentElapsedTime = positionEvent.positionTime
|
|
401
567
|
self.updateNowPlayingInfo()
|
|
568
|
+
self.updatePlayerUI()
|
|
402
569
|
return .success
|
|
403
570
|
}
|
|
404
571
|
}
|
|
@@ -414,6 +581,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
414
581
|
DispatchQueue.main.async {
|
|
415
582
|
self.loadedImage = uiImage
|
|
416
583
|
self.updateNowPlayingInfo()
|
|
584
|
+
self.updatePlayerUI()
|
|
417
585
|
}
|
|
418
586
|
}.resume()
|
|
419
587
|
}
|
|
@@ -423,7 +591,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
423
591
|
|
|
424
592
|
@MainActor
|
|
425
593
|
func invalidate() {
|
|
426
|
-
|
|
594
|
+
updatePlayerUI()
|
|
427
595
|
updateNowPlayingInfo()
|
|
428
596
|
|
|
429
597
|
if loadedImage == nil, let image = config.image {
|
|
@@ -456,9 +624,10 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
456
624
|
commandCenter.pauseCommand.removeTarget(nil)
|
|
457
625
|
commandCenter.skipForwardCommand.removeTarget(nil)
|
|
458
626
|
commandCenter.skipBackwardCommand.removeTarget(nil)
|
|
627
|
+
commandCenter.nextTrackCommand.removeTarget(nil)
|
|
628
|
+
commandCenter.previousTrackCommand.removeTarget(nil)
|
|
459
629
|
commandCenter.changePlaybackPositionCommand.removeTarget(nil)
|
|
460
630
|
|
|
461
|
-
// Clear now playing info so CarPlay hides the Now Playing bar
|
|
462
631
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
|
|
463
632
|
MPNowPlayingInfoCenter.default().playbackState = .stopped
|
|
464
633
|
}
|
|
@@ -466,19 +635,14 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
466
635
|
@MainActor
|
|
467
636
|
func updatePlaybackState(isPlaying: Bool) {
|
|
468
637
|
config.isPlaying = isPlaying
|
|
469
|
-
|
|
470
|
-
// Ensure AVAudioSession is active — required for CarPlay Now Playing bar
|
|
471
638
|
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
472
639
|
|
|
473
|
-
// If constructor's DispatchQueue.main.async hasn't run yet,
|
|
474
|
-
// nowPlayingInfo could be nil. Set it up now to fix the race condition.
|
|
475
640
|
if !isSetupComplete {
|
|
476
|
-
|
|
641
|
+
updatePlayerUI()
|
|
477
642
|
updateNowPlayingInfo()
|
|
478
643
|
isSetupComplete = true
|
|
479
644
|
}
|
|
480
645
|
|
|
481
|
-
// Update playback rate — use existing info or create fresh if nil
|
|
482
646
|
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
|
|
483
647
|
|
|
484
648
|
if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
|
|
@@ -501,6 +665,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
501
665
|
config.title = AutoText(text: title, distance: nil, duration: nil)
|
|
502
666
|
config.subtitle = AutoText(text: subtitle, distance: nil, duration: nil)
|
|
503
667
|
updateNowPlayingInfo()
|
|
668
|
+
updatePlayerUI()
|
|
504
669
|
}
|
|
505
670
|
|
|
506
671
|
@MainActor
|
|
@@ -508,7 +673,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
508
673
|
self.currentElapsedTime = elapsedTime
|
|
509
674
|
self.currentDuration = duration
|
|
510
675
|
|
|
511
|
-
// Update time-related fields — use existing info or create fresh if nil
|
|
512
676
|
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
|
|
513
677
|
|
|
514
678
|
if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
|
|
@@ -523,5 +687,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
523
687
|
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTime
|
|
524
688
|
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
|
|
525
689
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|
|
690
|
+
|
|
691
|
+
updatePlayerUI()
|
|
526
692
|
}
|
|
527
693
|
}
|
|
@@ -13,6 +13,8 @@ export interface NitroNowPlayingTemplateConfig extends TemplateConfig {
|
|
|
13
13
|
onPause?: () => void;
|
|
14
14
|
onSkipForward?: () => void;
|
|
15
15
|
onSkipBackward?: () => void;
|
|
16
|
+
onNextTrack?: () => void;
|
|
17
|
+
onPreviousTrack?: () => void;
|
|
16
18
|
onComplete?: () => void;
|
|
17
19
|
onProgressUpdate?: (currentTime: number, duration: number) => void;
|
|
18
20
|
onPlaybackFinished?: () => void;
|
|
@@ -85,6 +85,10 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
85
85
|
jni::local_ref<JFunc_void::javaobject> onSkipForward = this->getFieldValue(fieldOnSkipForward);
|
|
86
86
|
static const auto fieldOnSkipBackward = clazz->getField<JFunc_void::javaobject>("onSkipBackward");
|
|
87
87
|
jni::local_ref<JFunc_void::javaobject> onSkipBackward = this->getFieldValue(fieldOnSkipBackward);
|
|
88
|
+
static const auto fieldOnNextTrack = clazz->getField<JFunc_void::javaobject>("onNextTrack");
|
|
89
|
+
jni::local_ref<JFunc_void::javaobject> onNextTrack = this->getFieldValue(fieldOnNextTrack);
|
|
90
|
+
static const auto fieldOnPreviousTrack = clazz->getField<JFunc_void::javaobject>("onPreviousTrack");
|
|
91
|
+
jni::local_ref<JFunc_void::javaobject> onPreviousTrack = this->getFieldValue(fieldOnPreviousTrack);
|
|
88
92
|
static const auto fieldOnComplete = clazz->getField<JFunc_void::javaobject>("onComplete");
|
|
89
93
|
jni::local_ref<JFunc_void::javaobject> onComplete = this->getFieldValue(fieldOnComplete);
|
|
90
94
|
static const auto fieldOnProgressUpdate = clazz->getField<JFunc_void_double_double::javaobject>("onProgressUpdate");
|
|
@@ -181,6 +185,24 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
181
185
|
return JNICallable<JFunc_void, void()>(std::move(onSkipBackwardRef));
|
|
182
186
|
}
|
|
183
187
|
}()) : std::nullopt,
|
|
188
|
+
onNextTrack != nullptr ? std::make_optional([&]() -> std::function<void()> {
|
|
189
|
+
if (onNextTrack->isInstanceOf(JFunc_void_cxx::javaClassStatic())) [[likely]] {
|
|
190
|
+
auto downcast = jni::static_ref_cast<JFunc_void_cxx::javaobject>(onNextTrack);
|
|
191
|
+
return downcast->cthis()->getFunction();
|
|
192
|
+
} else {
|
|
193
|
+
auto onNextTrackRef = jni::make_global(onNextTrack);
|
|
194
|
+
return JNICallable<JFunc_void, void()>(std::move(onNextTrackRef));
|
|
195
|
+
}
|
|
196
|
+
}()) : std::nullopt,
|
|
197
|
+
onPreviousTrack != nullptr ? std::make_optional([&]() -> std::function<void()> {
|
|
198
|
+
if (onPreviousTrack->isInstanceOf(JFunc_void_cxx::javaClassStatic())) [[likely]] {
|
|
199
|
+
auto downcast = jni::static_ref_cast<JFunc_void_cxx::javaobject>(onPreviousTrack);
|
|
200
|
+
return downcast->cthis()->getFunction();
|
|
201
|
+
} else {
|
|
202
|
+
auto onPreviousTrackRef = jni::make_global(onPreviousTrack);
|
|
203
|
+
return JNICallable<JFunc_void, void()>(std::move(onPreviousTrackRef));
|
|
204
|
+
}
|
|
205
|
+
}()) : std::nullopt,
|
|
184
206
|
onComplete != nullptr ? std::make_optional([&]() -> std::function<void()> {
|
|
185
207
|
if (onComplete->isInstanceOf(JFunc_void_cxx::javaClassStatic())) [[likely]] {
|
|
186
208
|
auto downcast = jni::static_ref_cast<JFunc_void_cxx::javaobject>(onComplete);
|
|
@@ -217,7 +239,7 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
217
239
|
*/
|
|
218
240
|
[[maybe_unused]]
|
|
219
241
|
static jni::local_ref<JNowPlayingTemplateConfig::javaobject> fromCpp(const NowPlayingTemplateConfig& value) {
|
|
220
|
-
using JSignature = JNowPlayingTemplateConfig(jni::alias_ref<jni::JString>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<jni::JDouble>, jni::alias_ref<JAutoText>, jni::alias_ref<JAutoText>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<JVariant_GlyphImage_AssetImage>, jboolean, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void_double_double::javaobject>, jni::alias_ref<JFunc_void::javaobject>);
|
|
242
|
+
using JSignature = JNowPlayingTemplateConfig(jni::alias_ref<jni::JString>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void_std__optional_bool_::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<jni::JDouble>, jni::alias_ref<JAutoText>, jni::alias_ref<JAutoText>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<JVariant_GlyphImage_AssetImage>, jboolean, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void::javaobject>, jni::alias_ref<JFunc_void_double_double::javaobject>, jni::alias_ref<JFunc_void::javaobject>);
|
|
221
243
|
static const auto clazz = javaClassStatic();
|
|
222
244
|
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
|
223
245
|
return create(
|
|
@@ -239,6 +261,8 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
239
261
|
value.onPause.has_value() ? JFunc_void_cxx::fromCpp(value.onPause.value()) : nullptr,
|
|
240
262
|
value.onSkipForward.has_value() ? JFunc_void_cxx::fromCpp(value.onSkipForward.value()) : nullptr,
|
|
241
263
|
value.onSkipBackward.has_value() ? JFunc_void_cxx::fromCpp(value.onSkipBackward.value()) : nullptr,
|
|
264
|
+
value.onNextTrack.has_value() ? JFunc_void_cxx::fromCpp(value.onNextTrack.value()) : nullptr,
|
|
265
|
+
value.onPreviousTrack.has_value() ? JFunc_void_cxx::fromCpp(value.onPreviousTrack.value()) : nullptr,
|
|
242
266
|
value.onComplete.has_value() ? JFunc_void_cxx::fromCpp(value.onComplete.value()) : nullptr,
|
|
243
267
|
value.onProgressUpdate.has_value() ? JFunc_void_double_double_cxx::fromCpp(value.onProgressUpdate.value()) : nullptr,
|
|
244
268
|
value.onPlaybackFinished.has_value() ? JFunc_void_cxx::fromCpp(value.onPlaybackFinished.value()) : nullptr
|
|
@@ -70,6 +70,12 @@ data class NowPlayingTemplateConfig(
|
|
|
70
70
|
val onSkipBackward: Func_void?,
|
|
71
71
|
@DoNotStrip
|
|
72
72
|
@Keep
|
|
73
|
+
val onNextTrack: Func_void?,
|
|
74
|
+
@DoNotStrip
|
|
75
|
+
@Keep
|
|
76
|
+
val onPreviousTrack: Func_void?,
|
|
77
|
+
@DoNotStrip
|
|
78
|
+
@Keep
|
|
73
79
|
val onComplete: Func_void?,
|
|
74
80
|
@DoNotStrip
|
|
75
81
|
@Keep
|
|
@@ -81,8 +87,8 @@ data class NowPlayingTemplateConfig(
|
|
|
81
87
|
/**
|
|
82
88
|
* Create a new instance of NowPlayingTemplateConfig from Kotlin
|
|
83
89
|
*/
|
|
84
|
-
constructor(id: String, onWillAppear: ((animated: Boolean?) -> Unit)?, onWillDisappear: ((animated: Boolean?) -> Unit)?, onDidAppear: ((animated: Boolean?) -> Unit)?, onDidDisappear: ((animated: Boolean?) -> Unit)?, onPopped: (() -> Unit)?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Boolean, onPlay: (() -> Unit)?, onPause: (() -> Unit)?, onSkipForward: (() -> Unit)?, onSkipBackward: (() -> Unit)?, onComplete: (() -> Unit)?, onProgressUpdate: ((currentTime: Double, duration: Double) -> Unit)?, onPlaybackFinished: (() -> Unit)?):
|
|
85
|
-
this(id, onWillAppear?.let { Func_void_std__optional_bool__java(it) }, onWillDisappear?.let { Func_void_std__optional_bool__java(it) }, onDidAppear?.let { Func_void_std__optional_bool__java(it) }, onDidDisappear?.let { Func_void_std__optional_bool__java(it) }, onPopped?.let { Func_void_java(it) }, autoDismissMs, title, subtitle, courseId, lessonId, image, isPlaying, onPlay?.let { Func_void_java(it) }, onPause?.let { Func_void_java(it) }, onSkipForward?.let { Func_void_java(it) }, onSkipBackward?.let { Func_void_java(it) }, onComplete?.let { Func_void_java(it) }, onProgressUpdate?.let { Func_void_double_double_java(it) }, onPlaybackFinished?.let { Func_void_java(it) })
|
|
90
|
+
constructor(id: String, onWillAppear: ((animated: Boolean?) -> Unit)?, onWillDisappear: ((animated: Boolean?) -> Unit)?, onDidAppear: ((animated: Boolean?) -> Unit)?, onDidDisappear: ((animated: Boolean?) -> Unit)?, onPopped: (() -> Unit)?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Boolean, onPlay: (() -> Unit)?, onPause: (() -> Unit)?, onSkipForward: (() -> Unit)?, onSkipBackward: (() -> Unit)?, onNextTrack: (() -> Unit)?, onPreviousTrack: (() -> Unit)?, onComplete: (() -> Unit)?, onProgressUpdate: ((currentTime: Double, duration: Double) -> Unit)?, onPlaybackFinished: (() -> Unit)?):
|
|
91
|
+
this(id, onWillAppear?.let { Func_void_std__optional_bool__java(it) }, onWillDisappear?.let { Func_void_std__optional_bool__java(it) }, onDidAppear?.let { Func_void_std__optional_bool__java(it) }, onDidDisappear?.let { Func_void_std__optional_bool__java(it) }, onPopped?.let { Func_void_java(it) }, autoDismissMs, title, subtitle, courseId, lessonId, image, isPlaying, onPlay?.let { Func_void_java(it) }, onPause?.let { Func_void_java(it) }, onSkipForward?.let { Func_void_java(it) }, onSkipBackward?.let { Func_void_java(it) }, onNextTrack?.let { Func_void_java(it) }, onPreviousTrack?.let { Func_void_java(it) }, onComplete?.let { Func_void_java(it) }, onProgressUpdate?.let { Func_void_double_double_java(it) }, onPlaybackFinished?.let { Func_void_java(it) })
|
|
86
92
|
|
|
87
93
|
private companion object {
|
|
88
94
|
/**
|
|
@@ -92,8 +98,8 @@ data class NowPlayingTemplateConfig(
|
|
|
92
98
|
@Keep
|
|
93
99
|
@Suppress("unused")
|
|
94
100
|
@JvmStatic
|
|
95
|
-
private fun fromCpp(id: String, onWillAppear: Func_void_std__optional_bool_?, onWillDisappear: Func_void_std__optional_bool_?, onDidAppear: Func_void_std__optional_bool_?, onDidDisappear: Func_void_std__optional_bool_?, onPopped: Func_void?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Boolean, onPlay: Func_void?, onPause: Func_void?, onSkipForward: Func_void?, onSkipBackward: Func_void?, onComplete: Func_void?, onProgressUpdate: Func_void_double_double?, onPlaybackFinished: Func_void?): NowPlayingTemplateConfig {
|
|
96
|
-
return NowPlayingTemplateConfig(id, onWillAppear, onWillDisappear, onDidAppear, onDidDisappear, onPopped, autoDismissMs, title, subtitle, courseId, lessonId, image, isPlaying, onPlay, onPause, onSkipForward, onSkipBackward, onComplete, onProgressUpdate, onPlaybackFinished)
|
|
101
|
+
private fun fromCpp(id: String, onWillAppear: Func_void_std__optional_bool_?, onWillDisappear: Func_void_std__optional_bool_?, onDidAppear: Func_void_std__optional_bool_?, onDidDisappear: Func_void_std__optional_bool_?, onPopped: Func_void?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Boolean, onPlay: Func_void?, onPause: Func_void?, onSkipForward: Func_void?, onSkipBackward: Func_void?, onNextTrack: Func_void?, onPreviousTrack: Func_void?, onComplete: Func_void?, onProgressUpdate: Func_void_double_double?, onPlaybackFinished: Func_void?): NowPlayingTemplateConfig {
|
|
102
|
+
return NowPlayingTemplateConfig(id, onWillAppear, onWillDisappear, onDidAppear, onDidDisappear, onPopped, autoDismissMs, title, subtitle, courseId, lessonId, image, isPlaying, onPlay, onPause, onSkipForward, onSkipBackward, onNextTrack, onPreviousTrack, onComplete, onProgressUpdate, onPlaybackFinished)
|
|
97
103
|
}
|
|
98
104
|
}
|
|
99
105
|
}
|
|
@@ -19,7 +19,7 @@ public extension NowPlayingTemplateConfig {
|
|
|
19
19
|
/**
|
|
20
20
|
* Create a new instance of `NowPlayingTemplateConfig`.
|
|
21
21
|
*/
|
|
22
|
-
init(id: String, onWillAppear: ((_ animated: Bool?) -> Void)?, onWillDisappear: ((_ animated: Bool?) -> Void)?, onDidAppear: ((_ animated: Bool?) -> Void)?, onDidDisappear: ((_ animated: Bool?) -> Void)?, onPopped: (() -> Void)?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Bool, onPlay: (() -> Void)?, onPause: (() -> Void)?, onSkipForward: (() -> Void)?, onSkipBackward: (() -> Void)?, onComplete: (() -> Void)?, onProgressUpdate: ((_ currentTime: Double, _ duration: Double) -> Void)?, onPlaybackFinished: (() -> Void)?) {
|
|
22
|
+
init(id: String, onWillAppear: ((_ animated: Bool?) -> Void)?, onWillDisappear: ((_ animated: Bool?) -> Void)?, onDidAppear: ((_ animated: Bool?) -> Void)?, onDidDisappear: ((_ animated: Bool?) -> Void)?, onPopped: (() -> Void)?, autoDismissMs: Double?, title: AutoText, subtitle: AutoText?, courseId: String, lessonId: String, image: Variant_GlyphImage_AssetImage?, isPlaying: Bool, onPlay: (() -> Void)?, onPause: (() -> Void)?, onSkipForward: (() -> Void)?, onSkipBackward: (() -> Void)?, onNextTrack: (() -> Void)?, onPreviousTrack: (() -> Void)?, onComplete: (() -> Void)?, onProgressUpdate: ((_ currentTime: Double, _ duration: Double) -> Void)?, onPlaybackFinished: (() -> Void)?) {
|
|
23
23
|
self.init(std.string(id), { () -> bridge.std__optional_std__function_void_std__optional_bool_____animated______ in
|
|
24
24
|
if let __unwrappedValue = onWillAppear {
|
|
25
25
|
return bridge.create_std__optional_std__function_void_std__optional_bool_____animated______({ () -> bridge.Func_void_std__optional_bool_ in
|
|
@@ -126,6 +126,24 @@ public extension NowPlayingTemplateConfig {
|
|
|
126
126
|
} else {
|
|
127
127
|
return .init()
|
|
128
128
|
}
|
|
129
|
+
}(), { () -> bridge.std__optional_std__function_void____ in
|
|
130
|
+
if let __unwrappedValue = onNextTrack {
|
|
131
|
+
return bridge.create_std__optional_std__function_void____({ () -> bridge.Func_void in
|
|
132
|
+
let __closureWrapper = Func_void(__unwrappedValue)
|
|
133
|
+
return bridge.create_Func_void(__closureWrapper.toUnsafe())
|
|
134
|
+
}())
|
|
135
|
+
} else {
|
|
136
|
+
return .init()
|
|
137
|
+
}
|
|
138
|
+
}(), { () -> bridge.std__optional_std__function_void____ in
|
|
139
|
+
if let __unwrappedValue = onPreviousTrack {
|
|
140
|
+
return bridge.create_std__optional_std__function_void____({ () -> bridge.Func_void in
|
|
141
|
+
let __closureWrapper = Func_void(__unwrappedValue)
|
|
142
|
+
return bridge.create_Func_void(__closureWrapper.toUnsafe())
|
|
143
|
+
}())
|
|
144
|
+
} else {
|
|
145
|
+
return .init()
|
|
146
|
+
}
|
|
129
147
|
}(), { () -> bridge.std__optional_std__function_void____ in
|
|
130
148
|
if let __unwrappedValue = onComplete {
|
|
131
149
|
return bridge.create_std__optional_std__function_void____({ () -> bridge.Func_void in
|
|
@@ -600,6 +618,70 @@ public extension NowPlayingTemplateConfig {
|
|
|
600
618
|
}
|
|
601
619
|
}
|
|
602
620
|
|
|
621
|
+
var onNextTrack: (() -> Void)? {
|
|
622
|
+
@inline(__always)
|
|
623
|
+
get {
|
|
624
|
+
return { () -> (() -> Void)? in
|
|
625
|
+
if bridge.has_value_std__optional_std__function_void____(self.__onNextTrack) {
|
|
626
|
+
let __unwrapped = bridge.get_std__optional_std__function_void____(self.__onNextTrack)
|
|
627
|
+
return { () -> () -> Void in
|
|
628
|
+
let __wrappedFunction = bridge.wrap_Func_void(__unwrapped)
|
|
629
|
+
return { () -> Void in
|
|
630
|
+
__wrappedFunction.call()
|
|
631
|
+
}
|
|
632
|
+
}()
|
|
633
|
+
} else {
|
|
634
|
+
return nil
|
|
635
|
+
}
|
|
636
|
+
}()
|
|
637
|
+
}
|
|
638
|
+
@inline(__always)
|
|
639
|
+
set {
|
|
640
|
+
self.__onNextTrack = { () -> bridge.std__optional_std__function_void____ in
|
|
641
|
+
if let __unwrappedValue = newValue {
|
|
642
|
+
return bridge.create_std__optional_std__function_void____({ () -> bridge.Func_void in
|
|
643
|
+
let __closureWrapper = Func_void(__unwrappedValue)
|
|
644
|
+
return bridge.create_Func_void(__closureWrapper.toUnsafe())
|
|
645
|
+
}())
|
|
646
|
+
} else {
|
|
647
|
+
return .init()
|
|
648
|
+
}
|
|
649
|
+
}()
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
var onPreviousTrack: (() -> Void)? {
|
|
654
|
+
@inline(__always)
|
|
655
|
+
get {
|
|
656
|
+
return { () -> (() -> Void)? in
|
|
657
|
+
if bridge.has_value_std__optional_std__function_void____(self.__onPreviousTrack) {
|
|
658
|
+
let __unwrapped = bridge.get_std__optional_std__function_void____(self.__onPreviousTrack)
|
|
659
|
+
return { () -> () -> Void in
|
|
660
|
+
let __wrappedFunction = bridge.wrap_Func_void(__unwrapped)
|
|
661
|
+
return { () -> Void in
|
|
662
|
+
__wrappedFunction.call()
|
|
663
|
+
}
|
|
664
|
+
}()
|
|
665
|
+
} else {
|
|
666
|
+
return nil
|
|
667
|
+
}
|
|
668
|
+
}()
|
|
669
|
+
}
|
|
670
|
+
@inline(__always)
|
|
671
|
+
set {
|
|
672
|
+
self.__onPreviousTrack = { () -> bridge.std__optional_std__function_void____ in
|
|
673
|
+
if let __unwrappedValue = newValue {
|
|
674
|
+
return bridge.create_std__optional_std__function_void____({ () -> bridge.Func_void in
|
|
675
|
+
let __closureWrapper = Func_void(__unwrappedValue)
|
|
676
|
+
return bridge.create_Func_void(__closureWrapper.toUnsafe())
|
|
677
|
+
}())
|
|
678
|
+
} else {
|
|
679
|
+
return .init()
|
|
680
|
+
}
|
|
681
|
+
}()
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
603
685
|
var onComplete: (() -> Void)? {
|
|
604
686
|
@inline(__always)
|
|
605
687
|
get {
|
|
@@ -62,13 +62,15 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
62
62
|
std::optional<std::function<void()>> onPause SWIFT_PRIVATE;
|
|
63
63
|
std::optional<std::function<void()>> onSkipForward SWIFT_PRIVATE;
|
|
64
64
|
std::optional<std::function<void()>> onSkipBackward SWIFT_PRIVATE;
|
|
65
|
+
std::optional<std::function<void()>> onNextTrack SWIFT_PRIVATE;
|
|
66
|
+
std::optional<std::function<void()>> onPreviousTrack SWIFT_PRIVATE;
|
|
65
67
|
std::optional<std::function<void()>> onComplete SWIFT_PRIVATE;
|
|
66
68
|
std::optional<std::function<void(double /* currentTime */, double /* duration */)>> onProgressUpdate SWIFT_PRIVATE;
|
|
67
69
|
std::optional<std::function<void()>> onPlaybackFinished SWIFT_PRIVATE;
|
|
68
70
|
|
|
69
71
|
public:
|
|
70
72
|
NowPlayingTemplateConfig() = default;
|
|
71
|
-
explicit NowPlayingTemplateConfig(std::string id, std::optional<std::function<void(std::optional<bool> /* animated */)>> onWillAppear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onWillDisappear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onDidAppear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onDidDisappear, std::optional<std::function<void()>> onPopped, std::optional<double> autoDismissMs, AutoText title, std::optional<AutoText> subtitle, std::string courseId, std::string lessonId, std::optional<std::variant<GlyphImage, AssetImage>> image, bool isPlaying, std::optional<std::function<void()>> onPlay, std::optional<std::function<void()>> onPause, std::optional<std::function<void()>> onSkipForward, std::optional<std::function<void()>> onSkipBackward, std::optional<std::function<void()>> onComplete, std::optional<std::function<void(double /* currentTime */, double /* duration */)>> onProgressUpdate, std::optional<std::function<void()>> onPlaybackFinished): id(id), onWillAppear(onWillAppear), onWillDisappear(onWillDisappear), onDidAppear(onDidAppear), onDidDisappear(onDidDisappear), onPopped(onPopped), autoDismissMs(autoDismissMs), title(title), subtitle(subtitle), courseId(courseId), lessonId(lessonId), image(image), isPlaying(isPlaying), onPlay(onPlay), onPause(onPause), onSkipForward(onSkipForward), onSkipBackward(onSkipBackward), onComplete(onComplete), onProgressUpdate(onProgressUpdate), onPlaybackFinished(onPlaybackFinished) {}
|
|
73
|
+
explicit NowPlayingTemplateConfig(std::string id, std::optional<std::function<void(std::optional<bool> /* animated */)>> onWillAppear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onWillDisappear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onDidAppear, std::optional<std::function<void(std::optional<bool> /* animated */)>> onDidDisappear, std::optional<std::function<void()>> onPopped, std::optional<double> autoDismissMs, AutoText title, std::optional<AutoText> subtitle, std::string courseId, std::string lessonId, std::optional<std::variant<GlyphImage, AssetImage>> image, bool isPlaying, std::optional<std::function<void()>> onPlay, std::optional<std::function<void()>> onPause, std::optional<std::function<void()>> onSkipForward, std::optional<std::function<void()>> onSkipBackward, std::optional<std::function<void()>> onNextTrack, std::optional<std::function<void()>> onPreviousTrack, std::optional<std::function<void()>> onComplete, std::optional<std::function<void(double /* currentTime */, double /* duration */)>> onProgressUpdate, std::optional<std::function<void()>> onPlaybackFinished): id(id), onWillAppear(onWillAppear), onWillDisappear(onWillDisappear), onDidAppear(onDidAppear), onDidDisappear(onDidDisappear), onPopped(onPopped), autoDismissMs(autoDismissMs), title(title), subtitle(subtitle), courseId(courseId), lessonId(lessonId), image(image), isPlaying(isPlaying), onPlay(onPlay), onPause(onPause), onSkipForward(onSkipForward), onSkipBackward(onSkipBackward), onNextTrack(onNextTrack), onPreviousTrack(onPreviousTrack), onComplete(onComplete), onProgressUpdate(onProgressUpdate), onPlaybackFinished(onPlaybackFinished) {}
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
} // namespace margelo::nitro::swe::iternio::reactnativeautoplay
|
|
@@ -98,6 +100,8 @@ namespace margelo::nitro {
|
|
|
98
100
|
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onPause")),
|
|
99
101
|
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onSkipForward")),
|
|
100
102
|
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onSkipBackward")),
|
|
103
|
+
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onNextTrack")),
|
|
104
|
+
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onPreviousTrack")),
|
|
101
105
|
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onComplete")),
|
|
102
106
|
JSIConverter<std::optional<std::function<void(double, double)>>>::fromJSI(runtime, obj.getProperty(runtime, "onProgressUpdate")),
|
|
103
107
|
JSIConverter<std::optional<std::function<void()>>>::fromJSI(runtime, obj.getProperty(runtime, "onPlaybackFinished"))
|
|
@@ -122,6 +126,8 @@ namespace margelo::nitro {
|
|
|
122
126
|
obj.setProperty(runtime, "onPause", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onPause));
|
|
123
127
|
obj.setProperty(runtime, "onSkipForward", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onSkipForward));
|
|
124
128
|
obj.setProperty(runtime, "onSkipBackward", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onSkipBackward));
|
|
129
|
+
obj.setProperty(runtime, "onNextTrack", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onNextTrack));
|
|
130
|
+
obj.setProperty(runtime, "onPreviousTrack", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onPreviousTrack));
|
|
125
131
|
obj.setProperty(runtime, "onComplete", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onComplete));
|
|
126
132
|
obj.setProperty(runtime, "onProgressUpdate", JSIConverter<std::optional<std::function<void(double, double)>>>::toJSI(runtime, arg.onProgressUpdate));
|
|
127
133
|
obj.setProperty(runtime, "onPlaybackFinished", JSIConverter<std::optional<std::function<void()>>>::toJSI(runtime, arg.onPlaybackFinished));
|
|
@@ -152,6 +158,8 @@ namespace margelo::nitro {
|
|
|
152
158
|
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onPause"))) return false;
|
|
153
159
|
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onSkipForward"))) return false;
|
|
154
160
|
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onSkipBackward"))) return false;
|
|
161
|
+
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onNextTrack"))) return false;
|
|
162
|
+
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onPreviousTrack"))) return false;
|
|
155
163
|
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onComplete"))) return false;
|
|
156
164
|
if (!JSIConverter<std::optional<std::function<void(double, double)>>>::canConvert(runtime, obj.getProperty(runtime, "onProgressUpdate"))) return false;
|
|
157
165
|
if (!JSIConverter<std::optional<std::function<void()>>>::canConvert(runtime, obj.getProperty(runtime, "onPlaybackFinished"))) return false;
|
package/package.json
CHANGED
|
@@ -24,6 +24,8 @@ export interface NitroNowPlayingTemplateConfig extends TemplateConfig {
|
|
|
24
24
|
onPause?: () => void;
|
|
25
25
|
onSkipForward?: () => void;
|
|
26
26
|
onSkipBackward?: () => void;
|
|
27
|
+
onNextTrack?: () => void;
|
|
28
|
+
onPreviousTrack?: () => void;
|
|
27
29
|
onComplete?: () => void;
|
|
28
30
|
onProgressUpdate?: (currentTime: number, duration: number) => void;
|
|
29
31
|
onPlaybackFinished?: () => void;
|