@neoskola/auto-play 0.2.18 → 0.3.0
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/ios/hybrid/HybridNowPlayingTemplate.swift +90 -0
- package/ios/templates/NowPlayingTemplate.swift +191 -0
- package/lib/specs/NowPlayingTemplate.nitro.d.ts +6 -0
- package/lib/templates/NowPlayingTemplate.d.ts +8 -0
- package/lib/templates/NowPlayingTemplate.js +18 -0
- package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridNowPlayingTemplateSpec.cpp +92 -0
- package/nitrogen/generated/android/c++/JHybridNowPlayingTemplateSpec.hpp +6 -0
- package/nitrogen/generated/android/c++/JNowPlayingTemplateConfig.hpp +27 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridNowPlayingTemplateSpec.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NowPlayingTemplateConfig.kt +11 -5
- package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.hpp +58 -0
- package/nitrogen/generated/ios/c++/HybridNowPlayingTemplateSpecSwift.hpp +48 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNowPlayingTemplateSpec.swift +6 -0
- package/nitrogen/generated/ios/swift/HybridNowPlayingTemplateSpec_cxx.swift +114 -0
- package/nitrogen/generated/ios/swift/NowPlayingTemplateConfig.swift +83 -1
- package/nitrogen/generated/shared/c++/HybridNowPlayingTemplateSpec.cpp +6 -0
- package/nitrogen/generated/shared/c++/HybridNowPlayingTemplateSpec.hpp +6 -0
- package/nitrogen/generated/shared/c++/NowPlayingTemplateConfig.hpp +10 -2
- package/package.json +1 -1
- package/src/specs/NowPlayingTemplate.nitro.ts +8 -0
- package/src/templates/NowPlayingTemplate.ts +26 -0
|
@@ -58,4 +58,94 @@ class HybridNowPlayingTemplate: HybridNowPlayingTemplateSpec {
|
|
|
58
58
|
await template.updateElapsedTime(elapsedTime: elapsedTime, duration: duration)
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
// MARK: - Native Audio Playback
|
|
63
|
+
|
|
64
|
+
func playAudio(
|
|
65
|
+
templateId: String,
|
|
66
|
+
url: String,
|
|
67
|
+
startFrom: Double
|
|
68
|
+
) throws -> Promise<Bool> {
|
|
69
|
+
return Promise.async {
|
|
70
|
+
guard
|
|
71
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
72
|
+
as? NowPlayingTemplate
|
|
73
|
+
else {
|
|
74
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return await template.playAudio(url: url, startFrom: startFrom)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func pauseAudio(templateId: String) throws -> Promise<Void> {
|
|
82
|
+
return Promise.async {
|
|
83
|
+
guard
|
|
84
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
85
|
+
as? NowPlayingTemplate
|
|
86
|
+
else {
|
|
87
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await template.pauseAudio()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func resumeAudio(templateId: String) throws -> Promise<Void> {
|
|
95
|
+
return Promise.async {
|
|
96
|
+
guard
|
|
97
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
98
|
+
as? NowPlayingTemplate
|
|
99
|
+
else {
|
|
100
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await template.resumeAudio()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func seekForward(
|
|
108
|
+
templateId: String,
|
|
109
|
+
seconds: Double
|
|
110
|
+
) throws -> Promise<Void> {
|
|
111
|
+
return Promise.async {
|
|
112
|
+
guard
|
|
113
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
114
|
+
as? NowPlayingTemplate
|
|
115
|
+
else {
|
|
116
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await template.seekForward(seconds: seconds)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func seekBackward(
|
|
124
|
+
templateId: String,
|
|
125
|
+
seconds: Double
|
|
126
|
+
) throws -> Promise<Void> {
|
|
127
|
+
return Promise.async {
|
|
128
|
+
guard
|
|
129
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
130
|
+
as? NowPlayingTemplate
|
|
131
|
+
else {
|
|
132
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
await template.seekBackward(seconds: seconds)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func stopAudio(templateId: String) throws -> Promise<Void> {
|
|
140
|
+
return Promise.async {
|
|
141
|
+
guard
|
|
142
|
+
let template = TemplateStore.getTemplate(templateId: templateId)
|
|
143
|
+
as? NowPlayingTemplate
|
|
144
|
+
else {
|
|
145
|
+
throw AutoPlayError.templateNotFound(templateId)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await template.stopAudio()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
61
151
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import CarPlay
|
|
2
2
|
import MediaPlayer
|
|
3
|
+
import AVFoundation
|
|
3
4
|
|
|
4
5
|
class NowPlayingTemplate: AutoPlayTemplate {
|
|
5
6
|
var template: CPNowPlayingTemplate
|
|
@@ -9,6 +10,15 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
9
10
|
private var currentElapsedTime: Double = 0
|
|
10
11
|
private var currentDuration: Double = 0
|
|
11
12
|
|
|
13
|
+
// Native audio player
|
|
14
|
+
private var player: AVPlayer?
|
|
15
|
+
private var playerItem: AVPlayerItem?
|
|
16
|
+
private var timeObserver: Any?
|
|
17
|
+
private var progressReportTimer: Timer?
|
|
18
|
+
private var lastReportedSecond: Int = 0
|
|
19
|
+
private var didFinishObserver: NSObjectProtocol?
|
|
20
|
+
private var completionFired = false
|
|
21
|
+
|
|
12
22
|
var autoDismissMs: Double? {
|
|
13
23
|
return config.autoDismissMs
|
|
14
24
|
}
|
|
@@ -42,12 +52,182 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
42
52
|
}
|
|
43
53
|
}
|
|
44
54
|
|
|
55
|
+
// MARK: - Native Audio Playback
|
|
56
|
+
|
|
57
|
+
@MainActor
|
|
58
|
+
func playAudio(url: String, startFrom: Double) -> Bool {
|
|
59
|
+
// Clean up any existing player
|
|
60
|
+
cleanupPlayer()
|
|
61
|
+
completionFired = false
|
|
62
|
+
lastReportedSecond = Int(startFrom)
|
|
63
|
+
|
|
64
|
+
guard let audioURL = URL(string: url) else {
|
|
65
|
+
print("[NowPlayingTemplate] Invalid audio URL: \(url)")
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Ensure AVAudioSession is active
|
|
70
|
+
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
71
|
+
|
|
72
|
+
let asset = AVURLAsset(url: audioURL)
|
|
73
|
+
playerItem = AVPlayerItem(asset: asset)
|
|
74
|
+
player = AVPlayer(playerItem: playerItem)
|
|
75
|
+
|
|
76
|
+
// Seek to start position
|
|
77
|
+
if startFrom > 0 {
|
|
78
|
+
let time = CMTime(seconds: startFrom, preferredTimescale: 600)
|
|
79
|
+
player?.seek(to: time)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Periodic time observer (every 1 second) for MPNowPlayingInfoCenter updates
|
|
83
|
+
let interval = CMTime(seconds: 1.0, preferredTimescale: 600)
|
|
84
|
+
timeObserver = player?.addPeriodicTimeObserver(
|
|
85
|
+
forInterval: interval,
|
|
86
|
+
queue: .main
|
|
87
|
+
) { [weak self] time in
|
|
88
|
+
self?.handleTimeUpdate(time: time)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Playback finished notification
|
|
92
|
+
didFinishObserver = NotificationCenter.default.addObserver(
|
|
93
|
+
forName: .AVPlayerItemDidPlayToEndTime,
|
|
94
|
+
object: playerItem,
|
|
95
|
+
queue: .main
|
|
96
|
+
) { [weak self] _ in
|
|
97
|
+
self?.handlePlaybackFinished()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Progress report timer (every 30 seconds) — calls JS callback for backend reporting
|
|
101
|
+
progressReportTimer = Timer.scheduledTimer(
|
|
102
|
+
withTimeInterval: 30.0,
|
|
103
|
+
repeats: true
|
|
104
|
+
) { [weak self] _ in
|
|
105
|
+
self?.reportProgress()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Play
|
|
109
|
+
player?.play()
|
|
110
|
+
|
|
111
|
+
// Update NowPlaying UI
|
|
112
|
+
config.isPlaying = true
|
|
113
|
+
updateNowPlayingInfo()
|
|
114
|
+
MPNowPlayingInfoCenter.default().playbackState = .playing
|
|
115
|
+
|
|
116
|
+
print("[NowPlayingTemplate] Native audio playback started: \(url)")
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private func handleTimeUpdate(time: CMTime) {
|
|
121
|
+
let currentTime = CMTimeGetSeconds(time)
|
|
122
|
+
let duration = CMTimeGetSeconds(playerItem?.duration ?? .zero)
|
|
123
|
+
|
|
124
|
+
guard !currentTime.isNaN else { return }
|
|
125
|
+
|
|
126
|
+
currentElapsedTime = currentTime
|
|
127
|
+
if !duration.isNaN && duration > 0 {
|
|
128
|
+
currentDuration = duration
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Update MPNowPlayingInfoCenter — no JS roundtrip needed
|
|
132
|
+
if var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo {
|
|
133
|
+
if currentDuration > 0 {
|
|
134
|
+
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentDuration
|
|
135
|
+
}
|
|
136
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
|
|
137
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
|
|
138
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 95% completion check
|
|
142
|
+
if !completionFired && currentDuration > 0 && currentTime / currentDuration >= 0.95 {
|
|
143
|
+
completionFired = true
|
|
144
|
+
config.onComplete?()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private func reportProgress() {
|
|
149
|
+
let currentSecond = Int(currentElapsedTime)
|
|
150
|
+
guard currentSecond != lastReportedSecond && currentSecond > 0 else { return }
|
|
151
|
+
lastReportedSecond = currentSecond
|
|
152
|
+
config.onProgressUpdate?(currentElapsedTime, currentDuration)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private func handlePlaybackFinished() {
|
|
156
|
+
config.isPlaying = false
|
|
157
|
+
MPNowPlayingInfoCenter.default().playbackState = .stopped
|
|
158
|
+
// Fire completion if not already fired
|
|
159
|
+
if !completionFired {
|
|
160
|
+
completionFired = true
|
|
161
|
+
config.onComplete?()
|
|
162
|
+
}
|
|
163
|
+
config.onPlaybackFinished?()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@MainActor
|
|
167
|
+
func pauseAudio() {
|
|
168
|
+
player?.pause()
|
|
169
|
+
config.isPlaying = false
|
|
170
|
+
updatePlaybackState(isPlaying: false)
|
|
171
|
+
reportProgress()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@MainActor
|
|
175
|
+
func resumeAudio() {
|
|
176
|
+
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
177
|
+
player?.play()
|
|
178
|
+
config.isPlaying = true
|
|
179
|
+
updatePlaybackState(isPlaying: true)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@MainActor
|
|
183
|
+
func seekForward(seconds: Double) {
|
|
184
|
+
guard let player = player else { return }
|
|
185
|
+
let newTime = CMTimeGetSeconds(player.currentTime()) + seconds
|
|
186
|
+
let clampedTime = min(newTime, currentDuration)
|
|
187
|
+
player.seek(to: CMTime(seconds: clampedTime, preferredTimescale: 600))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@MainActor
|
|
191
|
+
func seekBackward(seconds: Double) {
|
|
192
|
+
guard let player = player else { return }
|
|
193
|
+
let newTime = CMTimeGetSeconds(player.currentTime()) - seconds
|
|
194
|
+
let clampedTime = max(newTime, 0)
|
|
195
|
+
player.seek(to: CMTime(seconds: clampedTime, preferredTimescale: 600))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@MainActor
|
|
199
|
+
func stopAudio() {
|
|
200
|
+
reportProgress()
|
|
201
|
+
cleanupPlayer()
|
|
202
|
+
config.isPlaying = false
|
|
203
|
+
MPNowPlayingInfoCenter.default().playbackState = .stopped
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private func cleanupPlayer() {
|
|
207
|
+
if let timeObserver = timeObserver {
|
|
208
|
+
player?.removeTimeObserver(timeObserver)
|
|
209
|
+
self.timeObserver = nil
|
|
210
|
+
}
|
|
211
|
+
if let didFinishObserver = didFinishObserver {
|
|
212
|
+
NotificationCenter.default.removeObserver(didFinishObserver)
|
|
213
|
+
self.didFinishObserver = nil
|
|
214
|
+
}
|
|
215
|
+
progressReportTimer?.invalidate()
|
|
216
|
+
progressReportTimer = nil
|
|
217
|
+
player?.pause()
|
|
218
|
+
player = nil
|
|
219
|
+
playerItem = nil
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// MARK: - CarPlay UI
|
|
223
|
+
|
|
45
224
|
private func setupNowPlayingButtons() {
|
|
46
225
|
var buttons: [CPNowPlayingButton] = []
|
|
47
226
|
|
|
48
227
|
let skipBackButton = CPNowPlayingImageButton(
|
|
49
228
|
image: UIImage(systemName: "gobackward.30")!
|
|
50
229
|
) { [weak self] _ in
|
|
230
|
+
self?.seekBackward(seconds: 30)
|
|
51
231
|
self?.config.onSkipBackward?()
|
|
52
232
|
}
|
|
53
233
|
buttons.append(skipBackButton)
|
|
@@ -55,6 +235,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
55
235
|
let skipForwardButton = CPNowPlayingImageButton(
|
|
56
236
|
image: UIImage(systemName: "goforward.30")!
|
|
57
237
|
) { [weak self] _ in
|
|
238
|
+
self?.seekForward(seconds: 30)
|
|
58
239
|
self?.config.onSkipForward?()
|
|
59
240
|
}
|
|
60
241
|
buttons.append(skipForwardButton)
|
|
@@ -97,6 +278,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
97
278
|
commandCenter.playCommand.isEnabled = true
|
|
98
279
|
commandCenter.playCommand.removeTarget(nil)
|
|
99
280
|
commandCenter.playCommand.addTarget { [weak self] _ in
|
|
281
|
+
self?.resumeAudio()
|
|
100
282
|
self?.config.onPlay?()
|
|
101
283
|
return .success
|
|
102
284
|
}
|
|
@@ -104,6 +286,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
104
286
|
commandCenter.pauseCommand.isEnabled = true
|
|
105
287
|
commandCenter.pauseCommand.removeTarget(nil)
|
|
106
288
|
commandCenter.pauseCommand.addTarget { [weak self] _ in
|
|
289
|
+
self?.pauseAudio()
|
|
107
290
|
self?.config.onPause?()
|
|
108
291
|
return .success
|
|
109
292
|
}
|
|
@@ -112,6 +295,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
112
295
|
commandCenter.skipForwardCommand.preferredIntervals = [30]
|
|
113
296
|
commandCenter.skipForwardCommand.removeTarget(nil)
|
|
114
297
|
commandCenter.skipForwardCommand.addTarget { [weak self] _ in
|
|
298
|
+
self?.seekForward(seconds: 30)
|
|
115
299
|
self?.config.onSkipForward?()
|
|
116
300
|
return .success
|
|
117
301
|
}
|
|
@@ -120,6 +304,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
120
304
|
commandCenter.skipBackwardCommand.preferredIntervals = [30]
|
|
121
305
|
commandCenter.skipBackwardCommand.removeTarget(nil)
|
|
122
306
|
commandCenter.skipBackwardCommand.addTarget { [weak self] _ in
|
|
307
|
+
self?.seekBackward(seconds: 30)
|
|
123
308
|
self?.config.onSkipBackward?()
|
|
124
309
|
return .success
|
|
125
310
|
}
|
|
@@ -131,6 +316,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
131
316
|
let positionEvent = event as? MPChangePlaybackPositionCommandEvent else {
|
|
132
317
|
return .commandFailed
|
|
133
318
|
}
|
|
319
|
+
let time = CMTime(seconds: positionEvent.positionTime, preferredTimescale: 600)
|
|
320
|
+
self.player?.seek(to: time)
|
|
134
321
|
self.currentElapsedTime = positionEvent.positionTime
|
|
135
322
|
self.updateNowPlayingInfo()
|
|
136
323
|
return .success
|
|
@@ -153,6 +340,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
153
340
|
}
|
|
154
341
|
}
|
|
155
342
|
|
|
343
|
+
// MARK: - AutoPlayTemplate Protocol
|
|
344
|
+
|
|
156
345
|
func invalidate() {
|
|
157
346
|
setupNowPlayingButtons()
|
|
158
347
|
updateNowPlayingInfo()
|
|
@@ -180,6 +369,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
180
369
|
|
|
181
370
|
func onPopped() {
|
|
182
371
|
config.onPopped?()
|
|
372
|
+
cleanupPlayer()
|
|
373
|
+
|
|
183
374
|
let commandCenter = MPRemoteCommandCenter.shared()
|
|
184
375
|
commandCenter.playCommand.removeTarget(nil)
|
|
185
376
|
commandCenter.pauseCommand.removeTarget(nil)
|
|
@@ -11,5 +11,11 @@ export interface NowPlayingTemplate extends HybridObject<{
|
|
|
11
11
|
updateNowPlayingTemplatePlaybackState(templateId: string, isPlaying: boolean): Promise<void>;
|
|
12
12
|
updateNowPlayingTemplateInfo(templateId: string, title: string, subtitle: string): Promise<void>;
|
|
13
13
|
updateNowPlayingTemplateElapsedTime(templateId: string, elapsedTime: number, duration: number): Promise<void>;
|
|
14
|
+
playAudio(templateId: string, url: string, startFrom: number): Promise<boolean>;
|
|
15
|
+
pauseAudio(templateId: string): Promise<void>;
|
|
16
|
+
resumeAudio(templateId: string): Promise<void>;
|
|
17
|
+
seekForward(templateId: string, seconds: number): Promise<void>;
|
|
18
|
+
seekBackward(templateId: string, seconds: number): Promise<void>;
|
|
19
|
+
stopAudio(templateId: string): Promise<void>;
|
|
14
20
|
}
|
|
15
21
|
export {};
|
|
@@ -14,6 +14,8 @@ export interface NitroNowPlayingTemplateConfig extends TemplateConfig {
|
|
|
14
14
|
onSkipForward?: () => void;
|
|
15
15
|
onSkipBackward?: () => void;
|
|
16
16
|
onComplete?: () => void;
|
|
17
|
+
onProgressUpdate?: (currentTime: number, duration: number) => void;
|
|
18
|
+
onPlaybackFinished?: () => void;
|
|
17
19
|
}
|
|
18
20
|
export type NowPlayingTemplateConfig = Omit<NitroNowPlayingTemplateConfig, 'image'> & {
|
|
19
21
|
image?: AutoImage;
|
|
@@ -23,4 +25,10 @@ export declare class NowPlayingTemplate extends Template<NowPlayingTemplateConfi
|
|
|
23
25
|
updatePlaybackState(isPlaying: boolean): Promise<void>;
|
|
24
26
|
updateNowPlayingInfo(title: string, subtitle: string): Promise<void>;
|
|
25
27
|
updateElapsedTime(elapsedTime: number, duration: number): Promise<void>;
|
|
28
|
+
playAudio(url: string, startFrom?: number): Promise<boolean>;
|
|
29
|
+
pauseAudio(): Promise<void>;
|
|
30
|
+
resumeAudio(): Promise<void>;
|
|
31
|
+
seekForward(seconds?: number): Promise<void>;
|
|
32
|
+
seekBackward(seconds?: number): Promise<void>;
|
|
33
|
+
stopAudio(): Promise<void>;
|
|
26
34
|
}
|
|
@@ -22,4 +22,22 @@ export class NowPlayingTemplate extends Template {
|
|
|
22
22
|
updateElapsedTime(elapsedTime, duration) {
|
|
23
23
|
return HybridNowPlayingTemplate.updateNowPlayingTemplateElapsedTime(this.id, elapsedTime, duration);
|
|
24
24
|
}
|
|
25
|
+
playAudio(url, startFrom = 0) {
|
|
26
|
+
return HybridNowPlayingTemplate.playAudio(this.id, url, startFrom);
|
|
27
|
+
}
|
|
28
|
+
pauseAudio() {
|
|
29
|
+
return HybridNowPlayingTemplate.pauseAudio(this.id);
|
|
30
|
+
}
|
|
31
|
+
resumeAudio() {
|
|
32
|
+
return HybridNowPlayingTemplate.resumeAudio(this.id);
|
|
33
|
+
}
|
|
34
|
+
seekForward(seconds = 30) {
|
|
35
|
+
return HybridNowPlayingTemplate.seekForward(this.id, seconds);
|
|
36
|
+
}
|
|
37
|
+
seekBackward(seconds = 30) {
|
|
38
|
+
return HybridNowPlayingTemplate.seekBackward(this.id, seconds);
|
|
39
|
+
}
|
|
40
|
+
stopAudio() {
|
|
41
|
+
return HybridNowPlayingTemplate.stopAudio(this.id);
|
|
42
|
+
}
|
|
25
43
|
}
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
#include "JHybridMessageTemplateSpec.hpp"
|
|
46
46
|
#include "JHybridNeoSkolaTemplateSpec.hpp"
|
|
47
47
|
#include "JHybridNowPlayingTemplateSpec.hpp"
|
|
48
|
+
#include "JFunc_void_double_double.hpp"
|
|
48
49
|
#include "JHybridSearchTemplateSpec.hpp"
|
|
49
50
|
#include "JHybridSectionListTemplateSpec.hpp"
|
|
50
51
|
#include "JHybridSubscriptionGateTemplateSpec.hpp"
|
|
@@ -89,6 +90,7 @@ int initialize(JavaVM* vm) {
|
|
|
89
90
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridMessageTemplateSpec::registerNatives();
|
|
90
91
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridNeoSkolaTemplateSpec::registerNatives();
|
|
91
92
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridNowPlayingTemplateSpec::registerNatives();
|
|
93
|
+
margelo::nitro::swe::iternio::reactnativeautoplay::JFunc_void_double_double_cxx::registerNatives();
|
|
92
94
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridSearchTemplateSpec::registerNatives();
|
|
93
95
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridSectionListTemplateSpec::registerNatives();
|
|
94
96
|
margelo::nitro::swe::iternio::reactnativeautoplay::JHybridSubscriptionGateTemplateSpec::registerNatives();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JFunc_void_double_double.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © 2026 Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include <functional>
|
|
12
|
+
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <NitroModules/JNICallable.hpp>
|
|
15
|
+
|
|
16
|
+
namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
17
|
+
|
|
18
|
+
using namespace facebook;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents the Java/Kotlin callback `(currentTime: Double, duration: Double) -> Unit`.
|
|
22
|
+
* This can be passed around between C++ and Java/Kotlin.
|
|
23
|
+
*/
|
|
24
|
+
struct JFunc_void_double_double: public jni::JavaClass<JFunc_void_double_double> {
|
|
25
|
+
public:
|
|
26
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_double_double;";
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
/**
|
|
30
|
+
* Invokes the function this `JFunc_void_double_double` instance holds through JNI.
|
|
31
|
+
*/
|
|
32
|
+
void invoke(double currentTime, double duration) const {
|
|
33
|
+
static const auto method = javaClassStatic()->getMethod<void(double /* currentTime */, double /* duration */)>("invoke");
|
|
34
|
+
method(self(), currentTime, duration);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An implementation of Func_void_double_double that is backed by a C++ implementation (using `std::function<...>`)
|
|
40
|
+
*/
|
|
41
|
+
class JFunc_void_double_double_cxx final: public jni::HybridClass<JFunc_void_double_double_cxx, JFunc_void_double_double> {
|
|
42
|
+
public:
|
|
43
|
+
static jni::local_ref<JFunc_void_double_double::javaobject> fromCpp(const std::function<void(double /* currentTime */, double /* duration */)>& func) {
|
|
44
|
+
return JFunc_void_double_double_cxx::newObjectCxxArgs(func);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public:
|
|
48
|
+
/**
|
|
49
|
+
* Invokes the C++ `std::function<...>` this `JFunc_void_double_double_cxx` instance holds.
|
|
50
|
+
*/
|
|
51
|
+
void invoke_cxx(double currentTime, double duration) {
|
|
52
|
+
_func(currentTime, duration);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public:
|
|
56
|
+
[[nodiscard]]
|
|
57
|
+
inline const std::function<void(double /* currentTime */, double /* duration */)>& getFunction() const {
|
|
58
|
+
return _func;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public:
|
|
62
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_double_double_cxx;";
|
|
63
|
+
static void registerNatives() {
|
|
64
|
+
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_double_double_cxx::invoke_cxx)});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private:
|
|
68
|
+
explicit JFunc_void_double_double_cxx(const std::function<void(double /* currentTime */, double /* duration */)>& func): _func(func) { }
|
|
69
|
+
|
|
70
|
+
private:
|
|
71
|
+
friend HybridBase;
|
|
72
|
+
std::function<void(double /* currentTime */, double /* duration */)> _func;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
} // namespace margelo::nitro::swe::iternio::reactnativeautoplay
|
|
@@ -46,6 +46,7 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay { struct NitroColor;
|
|
|
46
46
|
#include "NitroColor.hpp"
|
|
47
47
|
#include "JNitroColor.hpp"
|
|
48
48
|
#include "JAssetImage.hpp"
|
|
49
|
+
#include "JFunc_void_double_double.hpp"
|
|
49
50
|
|
|
50
51
|
namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
51
52
|
|
|
@@ -128,5 +129,96 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
128
129
|
return __promise;
|
|
129
130
|
}();
|
|
130
131
|
}
|
|
132
|
+
std::shared_ptr<Promise<bool>> JHybridNowPlayingTemplateSpec::playAudio(const std::string& templateId, const std::string& url, double startFrom) {
|
|
133
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */, jni::alias_ref<jni::JString> /* url */, double /* startFrom */)>("playAudio");
|
|
134
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId), jni::make_jstring(url), startFrom);
|
|
135
|
+
return [&]() {
|
|
136
|
+
auto __promise = Promise<bool>::create();
|
|
137
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& __boxedResult) {
|
|
138
|
+
auto __result = jni::static_ref_cast<jni::JBoolean>(__boxedResult);
|
|
139
|
+
__promise->resolve(static_cast<bool>(__result->value()));
|
|
140
|
+
});
|
|
141
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
142
|
+
jni::JniException __jniError(__throwable);
|
|
143
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
144
|
+
});
|
|
145
|
+
return __promise;
|
|
146
|
+
}();
|
|
147
|
+
}
|
|
148
|
+
std::shared_ptr<Promise<void>> JHybridNowPlayingTemplateSpec::pauseAudio(const std::string& templateId) {
|
|
149
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */)>("pauseAudio");
|
|
150
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId));
|
|
151
|
+
return [&]() {
|
|
152
|
+
auto __promise = Promise<void>::create();
|
|
153
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
154
|
+
__promise->resolve();
|
|
155
|
+
});
|
|
156
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
157
|
+
jni::JniException __jniError(__throwable);
|
|
158
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
159
|
+
});
|
|
160
|
+
return __promise;
|
|
161
|
+
}();
|
|
162
|
+
}
|
|
163
|
+
std::shared_ptr<Promise<void>> JHybridNowPlayingTemplateSpec::resumeAudio(const std::string& templateId) {
|
|
164
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */)>("resumeAudio");
|
|
165
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId));
|
|
166
|
+
return [&]() {
|
|
167
|
+
auto __promise = Promise<void>::create();
|
|
168
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
169
|
+
__promise->resolve();
|
|
170
|
+
});
|
|
171
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
172
|
+
jni::JniException __jniError(__throwable);
|
|
173
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
174
|
+
});
|
|
175
|
+
return __promise;
|
|
176
|
+
}();
|
|
177
|
+
}
|
|
178
|
+
std::shared_ptr<Promise<void>> JHybridNowPlayingTemplateSpec::seekForward(const std::string& templateId, double seconds) {
|
|
179
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */, double /* seconds */)>("seekForward");
|
|
180
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId), seconds);
|
|
181
|
+
return [&]() {
|
|
182
|
+
auto __promise = Promise<void>::create();
|
|
183
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
184
|
+
__promise->resolve();
|
|
185
|
+
});
|
|
186
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
187
|
+
jni::JniException __jniError(__throwable);
|
|
188
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
189
|
+
});
|
|
190
|
+
return __promise;
|
|
191
|
+
}();
|
|
192
|
+
}
|
|
193
|
+
std::shared_ptr<Promise<void>> JHybridNowPlayingTemplateSpec::seekBackward(const std::string& templateId, double seconds) {
|
|
194
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */, double /* seconds */)>("seekBackward");
|
|
195
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId), seconds);
|
|
196
|
+
return [&]() {
|
|
197
|
+
auto __promise = Promise<void>::create();
|
|
198
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
199
|
+
__promise->resolve();
|
|
200
|
+
});
|
|
201
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
202
|
+
jni::JniException __jniError(__throwable);
|
|
203
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
204
|
+
});
|
|
205
|
+
return __promise;
|
|
206
|
+
}();
|
|
207
|
+
}
|
|
208
|
+
std::shared_ptr<Promise<void>> JHybridNowPlayingTemplateSpec::stopAudio(const std::string& templateId) {
|
|
209
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* templateId */)>("stopAudio");
|
|
210
|
+
auto __result = method(_javaPart, jni::make_jstring(templateId));
|
|
211
|
+
return [&]() {
|
|
212
|
+
auto __promise = Promise<void>::create();
|
|
213
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
214
|
+
__promise->resolve();
|
|
215
|
+
});
|
|
216
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
217
|
+
jni::JniException __jniError(__throwable);
|
|
218
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
219
|
+
});
|
|
220
|
+
return __promise;
|
|
221
|
+
}();
|
|
222
|
+
}
|
|
131
223
|
|
|
132
224
|
} // namespace margelo::nitro::swe::iternio::reactnativeautoplay
|
|
@@ -58,6 +58,12 @@ namespace margelo::nitro::swe::iternio::reactnativeautoplay {
|
|
|
58
58
|
std::shared_ptr<Promise<void>> updateNowPlayingTemplatePlaybackState(const std::string& templateId, bool isPlaying) override;
|
|
59
59
|
std::shared_ptr<Promise<void>> updateNowPlayingTemplateInfo(const std::string& templateId, const std::string& title, const std::string& subtitle) override;
|
|
60
60
|
std::shared_ptr<Promise<void>> updateNowPlayingTemplateElapsedTime(const std::string& templateId, double elapsedTime, double duration) override;
|
|
61
|
+
std::shared_ptr<Promise<bool>> playAudio(const std::string& templateId, const std::string& url, double startFrom) override;
|
|
62
|
+
std::shared_ptr<Promise<void>> pauseAudio(const std::string& templateId) override;
|
|
63
|
+
std::shared_ptr<Promise<void>> resumeAudio(const std::string& templateId) override;
|
|
64
|
+
std::shared_ptr<Promise<void>> seekForward(const std::string& templateId, double seconds) override;
|
|
65
|
+
std::shared_ptr<Promise<void>> seekBackward(const std::string& templateId, double seconds) override;
|
|
66
|
+
std::shared_ptr<Promise<void>> stopAudio(const std::string& templateId) override;
|
|
61
67
|
|
|
62
68
|
private:
|
|
63
69
|
friend HybridBase;
|