@capgo/native-audio 8.3.15 → 8.3.17
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/build.gradle
CHANGED
|
@@ -78,7 +78,7 @@ dependencies {
|
|
|
78
78
|
implementation 'androidx.media3:media3-exoplayer-hls:1.10.0'
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
implementation 'androidx.media3:media3-session:1.
|
|
81
|
+
implementation 'androidx.media3:media3-session:1.10.0'
|
|
82
82
|
implementation 'androidx.media3:media3-transformer:1.9.2'
|
|
83
83
|
implementation 'androidx.media3:media3-ui:1.9.2'
|
|
84
84
|
implementation 'androidx.media3:media3-database:1.10.0'
|
|
@@ -34,14 +34,17 @@ extension AudioAsset {
|
|
|
34
34
|
scheduleLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
fileprivate func scheduleLocalFadeOutStopOnMain(audio: AVAudioPlayer) {
|
|
37
|
+
fileprivate func scheduleLocalFadeOutStopOnMain(audio: AVAudioPlayer, beforeStop: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
38
38
|
DispatchQueue.main.async { [weak self] in
|
|
39
39
|
guard let self else { return }
|
|
40
|
-
self.performLocalFadeOutStopOnMain(audio: audio)
|
|
40
|
+
self.performLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
fileprivate func performLocalFadeOutStopOnMain(audio: AVAudioPlayer) {
|
|
44
|
+
fileprivate func performLocalFadeOutStopOnMain(audio: AVAudioPlayer, beforeStop: ((TimeInterval, TimeInterval) -> Void)?) {
|
|
45
|
+
let elapsed = audio.currentTime
|
|
46
|
+
let duration = audio.duration.isFinite ? audio.duration : 0
|
|
47
|
+
beforeStop?(elapsed, duration)
|
|
45
48
|
audio.stop()
|
|
46
49
|
dispatchComplete()
|
|
47
50
|
}
|
|
@@ -71,12 +74,16 @@ extension AudioAsset {
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
77
|
+
/// - Parameter beforeStop: Called on the main queue immediately before `stop()` when `toPause` is false,
|
|
78
|
+
/// so the plugin can refresh Now Playing (rate 0) at the final elapsed time.
|
|
74
79
|
/// - Parameter beforePause: Called on the main queue immediately before `pause()` when `toPause` is true,
|
|
75
80
|
/// so the plugin can persist `timeBeforePause` and update Now Playing at the actual stop position.
|
|
81
|
+
/// Kept last so call sites can use a trailing closure for fade-out-to-pause.
|
|
76
82
|
func fadeOut(
|
|
77
83
|
audio: AVAudioPlayer,
|
|
78
84
|
fadeOutDuration: TimeInterval,
|
|
79
85
|
toPause: Bool = false,
|
|
86
|
+
beforeStop: ((TimeInterval, TimeInterval) -> Void)? = nil,
|
|
80
87
|
beforePause: ((TimeInterval, TimeInterval) -> Void)? = nil
|
|
81
88
|
) {
|
|
82
89
|
cancelFade()
|
|
@@ -85,7 +92,7 @@ extension AudioAsset {
|
|
|
85
92
|
if toPause {
|
|
86
93
|
scheduleLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
87
94
|
} else {
|
|
88
|
-
scheduleLocalFadeOutStopOnMain(audio: audio)
|
|
95
|
+
scheduleLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
89
96
|
}
|
|
90
97
|
return
|
|
91
98
|
}
|
|
@@ -108,7 +115,7 @@ extension AudioAsset {
|
|
|
108
115
|
if toPause {
|
|
109
116
|
self.performLocalFadeOutPauseOnMain(audio: audio, beforePause: beforePause)
|
|
110
117
|
} else {
|
|
111
|
-
self.performLocalFadeOutStopOnMain(audio: audio)
|
|
118
|
+
self.performLocalFadeOutStopOnMain(audio: audio, beforeStop: beforeStop)
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
121
|
}
|
|
@@ -261,10 +261,10 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
|
261
261
|
if player.isPlaying {
|
|
262
262
|
if toPause {
|
|
263
263
|
if player.volume > 0 {
|
|
264
|
-
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: true
|
|
264
|
+
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: true, beforePause: { [weak self] elapsed, duration in
|
|
265
265
|
guard let self, let owner = self.owner else { return }
|
|
266
266
|
owner.recordPausePositionAfterFade(assetId: self.assetId, elapsedTime: elapsed, duration: duration)
|
|
267
|
-
}
|
|
267
|
+
})
|
|
268
268
|
} else {
|
|
269
269
|
cancelFade()
|
|
270
270
|
schedulePauseWithPositionRecording(audio: player) { [weak self] elapsed, duration in
|
|
@@ -273,9 +273,15 @@ public class AudioAsset: NSObject, AVAudioPlayerDelegate {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
} else if player.volume > 0 {
|
|
276
|
-
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: false
|
|
276
|
+
fadeOut(audio: player, fadeOutDuration: fadeOutDuration, toPause: false, beforeStop: { [weak self] elapsed, duration in
|
|
277
|
+
guard let self, let owner = self.owner else { return }
|
|
278
|
+
owner.recordStoppedPlaybackStateAfterFade(assetId: self.assetId, elapsedTime: elapsed, duration: duration)
|
|
279
|
+
})
|
|
277
280
|
} else {
|
|
281
|
+
let elapsed = player.currentTime
|
|
282
|
+
let duration = player.duration.isFinite ? player.duration : 0
|
|
278
283
|
stop()
|
|
284
|
+
owner?.recordStoppedPlaybackStateAfterFade(assetId: assetId, elapsedTime: elapsed, duration: duration)
|
|
279
285
|
}
|
|
280
286
|
} else if !toPause {
|
|
281
287
|
stop()
|
|
@@ -12,7 +12,7 @@ enum MyError: Error {
|
|
|
12
12
|
@objc(NativeAudio)
|
|
13
13
|
// swiftlint:disable:next type_body_length
|
|
14
14
|
public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
15
|
-
private let pluginVersion: String = "8.3.
|
|
15
|
+
private let pluginVersion: String = "8.3.17"
|
|
16
16
|
public let identifier = "NativeAudio"
|
|
17
17
|
public let jsName = "NativeAudio"
|
|
18
18
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -266,8 +266,20 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
266
266
|
return
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
// Sample before `stop()` — `AudioAsset.stop()` resets every channel's `currentTime` to 0.
|
|
270
|
+
let elapsedTime = asset.getCurrentTime()
|
|
271
|
+
let duration = asset.getDuration()
|
|
269
272
|
asset.stop()
|
|
270
|
-
|
|
273
|
+
// Keep `currentlyPlayingAssetId` and Now Playing metadata so the lock screen card
|
|
274
|
+
// stays until `unload()` (or natural completion / another `play()` replaces it).
|
|
275
|
+
if self.showNotification,
|
|
276
|
+
self.currentlyPlayingAssetId == assetId {
|
|
277
|
+
self.updatePlaybackState(
|
|
278
|
+
isPlaying: false,
|
|
279
|
+
elapsedTime: elapsedTime,
|
|
280
|
+
duration: duration
|
|
281
|
+
)
|
|
282
|
+
}
|
|
271
283
|
}
|
|
272
284
|
return .success
|
|
273
285
|
}
|
|
@@ -974,7 +986,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
974
986
|
|
|
975
987
|
/// Stops playback of the audio asset identified by `assetId` from the plugin call and performs related cleanup.
|
|
976
988
|
///
|
|
977
|
-
/// The `assetId` is read from the call using `Constant.AssetIdKey`. If the asset is currently playing it will be stopped
|
|
989
|
+
/// The `assetId` is read from the call using `Constant.AssetIdKey`. If the asset is currently playing it will be stopped. When `showNotification` is enabled and this asset owns Now Playing, playback state is updated to stopped but the Now Playing card is left in place until `unload()` or natural completion. If the asset was created by `playOnce`, it is removed from `playOnceAssets` and its notification metadata is removed. The audio session is ended if appropriate. The call is resolved on success or rejected with an error message on failure.
|
|
978
990
|
@objc func stop(_ call: CAPPluginCall) {
|
|
979
991
|
let audioId = call.getString(Constant.AssetIdKey) ?? ""
|
|
980
992
|
let fadeOut = call.getBool(Constant.FadeOut) ?? false
|
|
@@ -989,11 +1001,32 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
989
1001
|
}
|
|
990
1002
|
|
|
991
1003
|
do {
|
|
1004
|
+
// Sample before `stopAudio` — non-fade `AudioAsset.stop()` resets `currentTime` to 0.
|
|
1005
|
+
var preStopNowPlayingElapsed: TimeInterval?
|
|
1006
|
+
var preStopNowPlayingDuration: TimeInterval?
|
|
1007
|
+
if !fadeOut,
|
|
1008
|
+
self.showNotification,
|
|
1009
|
+
self.currentlyPlayingAssetId == audioId,
|
|
1010
|
+
let preStopAsset = self.audioList[audioId] as? AudioAsset {
|
|
1011
|
+
preStopNowPlayingElapsed = preStopAsset.getCurrentTime()
|
|
1012
|
+
preStopNowPlayingDuration = preStopAsset.getDuration()
|
|
1013
|
+
}
|
|
1014
|
+
|
|
992
1015
|
try self.stopAudio(audioId: audioId, fadeOut: fadeOut, fadeOutDuration: fadeOutDuration)
|
|
993
1016
|
|
|
994
|
-
//
|
|
995
|
-
|
|
996
|
-
|
|
1017
|
+
// Keep `currentlyPlayingAssetId` so lock screen / Control Center stays tied to this asset
|
|
1018
|
+
// until `unload()` clears it; refresh Now Playing to a stopped state (rate 0).
|
|
1019
|
+
// Skip when fading out to stop: `recordStoppedPlaybackStateAfterFade` runs when the fade finishes
|
|
1020
|
+
// (and for zero-volume immediate stop inside `stopWithFade`).
|
|
1021
|
+
if let elapsed = preStopNowPlayingElapsed,
|
|
1022
|
+
let duration = preStopNowPlayingDuration,
|
|
1023
|
+
self.showNotification,
|
|
1024
|
+
self.currentlyPlayingAssetId == audioId {
|
|
1025
|
+
self.updatePlaybackState(
|
|
1026
|
+
isPlaying: false,
|
|
1027
|
+
elapsedTime: elapsed,
|
|
1028
|
+
duration: duration
|
|
1029
|
+
)
|
|
997
1030
|
}
|
|
998
1031
|
|
|
999
1032
|
// Clean up playOnce tracking if this was a playOnce asset
|
|
@@ -1517,6 +1550,16 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
1517
1550
|
}
|
|
1518
1551
|
}
|
|
1519
1552
|
|
|
1553
|
+
/// Refreshes Now Playing to a stopped state after fade-out-to-stop completes (or zero-volume stop-with-fade).
|
|
1554
|
+
internal func recordStoppedPlaybackStateAfterFade(assetId: String, elapsedTime: TimeInterval, duration: TimeInterval) {
|
|
1555
|
+
audioQueue.async { [weak self] in
|
|
1556
|
+
guard let self else { return }
|
|
1557
|
+
if self.showNotification && self.currentlyPlayingAssetId == assetId {
|
|
1558
|
+
self.updatePlaybackState(isPlaying: false, elapsedTime: elapsedTime, duration: duration)
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1520
1563
|
private func updatePlaybackState(isPlaying: Bool, elapsedTime: TimeInterval? = nil, duration: TimeInterval? = nil) {
|
|
1521
1564
|
DispatchQueue.main.async {
|
|
1522
1565
|
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
|