@neoskola/auto-play 0.2.17 → 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.
Files changed (28) hide show
  1. package/ios/hybrid/HybridNowPlayingTemplate.swift +90 -0
  2. package/ios/scenes/AutoPlayInterfaceController.swift +2 -2
  3. package/ios/templates/NowPlayingTemplate.swift +191 -0
  4. package/lib/specs/NowPlayingTemplate.nitro.d.ts +6 -0
  5. package/lib/templates/NowPlayingTemplate.d.ts +8 -0
  6. package/lib/templates/NowPlayingTemplate.js +18 -0
  7. package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +2 -0
  8. package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
  9. package/nitrogen/generated/android/c++/JHybridNowPlayingTemplateSpec.cpp +92 -0
  10. package/nitrogen/generated/android/c++/JHybridNowPlayingTemplateSpec.hpp +6 -0
  11. package/nitrogen/generated/android/c++/JNowPlayingTemplateConfig.hpp +27 -2
  12. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_double_double.kt +80 -0
  13. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridNowPlayingTemplateSpec.kt +24 -0
  14. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/NowPlayingTemplateConfig.kt +11 -5
  15. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.cpp +8 -0
  16. package/nitrogen/generated/ios/ReactNativeAutoPlay-Swift-Cxx-Bridge.hpp +58 -0
  17. package/nitrogen/generated/ios/c++/HybridNowPlayingTemplateSpecSwift.hpp +48 -0
  18. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  19. package/nitrogen/generated/ios/swift/Func_void_double_double.swift +47 -0
  20. package/nitrogen/generated/ios/swift/HybridNowPlayingTemplateSpec.swift +6 -0
  21. package/nitrogen/generated/ios/swift/HybridNowPlayingTemplateSpec_cxx.swift +114 -0
  22. package/nitrogen/generated/ios/swift/NowPlayingTemplateConfig.swift +83 -1
  23. package/nitrogen/generated/shared/c++/HybridNowPlayingTemplateSpec.cpp +6 -0
  24. package/nitrogen/generated/shared/c++/HybridNowPlayingTemplateSpec.hpp +6 -0
  25. package/nitrogen/generated/shared/c++/NowPlayingTemplateConfig.hpp +10 -2
  26. package/package.json +1 -1
  27. package/src/specs/NowPlayingTemplate.nitro.ts +8 -0
  28. 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
  }
@@ -74,9 +74,9 @@ class AutoPlayInterfaceController: NSObject, CPInterfaceControllerDelegate {
74
74
  /// The push call is made entirely in Objective-C so NSExceptions never propagate through Swift frames.
75
75
  private func pushNowPlayingTemplateSafely(animated: Bool) -> Bool {
76
76
  do {
77
- try ObjCExceptionCatcher.pushTemplate(
77
+ try ObjCExceptionCatcher.push(
78
78
  CPNowPlayingTemplate.shared,
79
- onInterfaceController: self.interfaceController,
79
+ on: self.interfaceController,
80
80
  animated: animated
81
81
  )
82
82
  print("[AutoPlay] CPNowPlayingTemplate pushed successfully")
@@ -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;