@javascriptcommon/react-native-track-player 1.2.9
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/CHANGELOG.md +75 -0
- package/LICENSE +21 -0
- package/android/build.gradle +34 -0
- package/android/proguard-rules.txt +3 -0
- package/android/react-native-music-control.iml +139 -0
- package/android/react-native-track-player.iml +151 -0
- package/android/src/main/AndroidManifest.xml +30 -0
- package/android/src/main/java/com/guichaguri/trackplayer/TrackPlayer.java +28 -0
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicEvents.java +55 -0
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicModule.java +298 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/HeadlessJsMediaService.java +174 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicBinder.java +47 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicManager.java +383 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicService.java +271 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/Utils.java +243 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/ButtonEvents.java +148 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/MetadataManager.java +379 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/models/Track.java +141 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/models/TrackType.java +35 -0
- package/android/src/main/res/drawable-hdpi/ic_forward.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_next.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_pause.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_play.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_previous.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_rewind.png +0 -0
- package/android/src/main/res/drawable-hdpi/ic_stop.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_forward.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_next.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_pause.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_play.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_previous.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_rewind.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_stop.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_forward.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_next.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_pause.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_play.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_previous.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_rewind.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_stop.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_forward.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_next.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_pause.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_play.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_previous.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_rewind.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_stop.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_forward.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_next.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_pause.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_play.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_previous.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_rewind.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_stop.png +0 -0
- package/index.d.ts +174 -0
- package/index.js +4 -0
- package/ios/RNTrackPlayer/Models/Capabilities.swift +17 -0
- package/ios/RNTrackPlayer/Models/MediaURL.swift +32 -0
- package/ios/RNTrackPlayer/Models/Track.swift +120 -0
- package/ios/RNTrackPlayer/RNTrackPlayer.swift +488 -0
- package/ios/RNTrackPlayer/RNTrackPlayerBridge.h +12 -0
- package/ios/RNTrackPlayer/RNTrackPlayerBridge.m +29 -0
- package/ios/RNTrackPlayer/Support/RNTrackPlayer-Bridging-Header.h +6 -0
- package/ios/TrackPlayer.xcodeproj/project.pbxproj +495 -0
- package/ios/TrackPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/TrackPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/TrackPlayer.xcodeproj/project.xcworkspace/xcuserdata/marco.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/TrackPlayer.xcodeproj/xcuserdata/marco.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/lib/ProgressComponent.js +70 -0
- package/lib/eventTypes.js +28 -0
- package/lib/hooks.js +160 -0
- package/lib/index.js +177 -0
- package/package.json +47 -0
- package/react-native-track-player.podspec +22 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RNTrackPlayer.swift
|
|
3
|
+
// RNTrackPlayer
|
|
4
|
+
//
|
|
5
|
+
// Created by David Chavez on 13.08.17.
|
|
6
|
+
// Copyright © 2017 David Chavez. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
import MediaPlayer
|
|
11
|
+
import React
|
|
12
|
+
|
|
13
|
+
@available(iOS 10.3, *)
|
|
14
|
+
@objc(RNTrackPlayer)
|
|
15
|
+
public class RNTrackPlayer: RCTEventEmitter {
|
|
16
|
+
|
|
17
|
+
// MARK: - Attributes
|
|
18
|
+
|
|
19
|
+
private var hasInitialized = false
|
|
20
|
+
|
|
21
|
+
// MARK: - Lifecycle Methods
|
|
22
|
+
|
|
23
|
+
deinit {
|
|
24
|
+
reset(resolve: { _ in }, reject: { _, _, _ in })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private var currentTrack: Track? = nil
|
|
28
|
+
|
|
29
|
+
private var previousArtworkUrl : String? = nil
|
|
30
|
+
|
|
31
|
+
private var placeHolderImageArtwork : MPMediaItemArtwork? = nil
|
|
32
|
+
|
|
33
|
+
private var artworkUrl : MediaURL? = nil
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// MARK: - RCTEventEmitter
|
|
37
|
+
|
|
38
|
+
override public static func requiresMainQueueSetup() -> Bool {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@objc(constantsToExport)
|
|
43
|
+
override public func constantsToExport() -> [AnyHashable: Any] {
|
|
44
|
+
return [
|
|
45
|
+
"STATE_NONE": PlayState.none.rawValue,
|
|
46
|
+
"STATE_PLAYING": PlayState.playing.rawValue,
|
|
47
|
+
"STATE_PAUSED": PlayState.paused.rawValue,
|
|
48
|
+
"STATE_STOPPED": PlayState.stopped.rawValue,
|
|
49
|
+
|
|
50
|
+
"CAPABILITY_PLAY": Capability.play.rawValue,
|
|
51
|
+
"CAPABILITY_PLAY_FROM_ID": "NOOP",
|
|
52
|
+
"CAPABILITY_PLAY_FROM_SEARCH": "NOOP",
|
|
53
|
+
"CAPABILITY_PAUSE": Capability.pause.rawValue,
|
|
54
|
+
"CAPABILITY_STOP": Capability.stop.rawValue,
|
|
55
|
+
"CAPABILITY_SEEK_TO": Capability.seek.rawValue,
|
|
56
|
+
"CAPABILITY_SKIP": "NOOP",
|
|
57
|
+
"CAPABILITY_SKIP_TO_NEXT": Capability.next.rawValue,
|
|
58
|
+
"CAPABILITY_SKIP_TO_PREVIOUS": Capability.previous.rawValue,
|
|
59
|
+
"CAPABILITY_SET_RATING": "NOOP",
|
|
60
|
+
"CAPABILITY_JUMP_FORWARD": Capability.jumpForward.rawValue,
|
|
61
|
+
"CAPABILITY_JUMP_BACKWARD": Capability.jumpBackward.rawValue,
|
|
62
|
+
"CAPABILITY_LIKE": Capability.like.rawValue,
|
|
63
|
+
"CAPABILITY_DISLIKE": Capability.dislike.rawValue,
|
|
64
|
+
"CAPABILITY_BOOKMARK": Capability.bookmark.rawValue,
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@objc(supportedEvents)
|
|
69
|
+
override public func supportedEvents() -> [String] {
|
|
70
|
+
return [
|
|
71
|
+
"playback-queue-ended",
|
|
72
|
+
"playback-state",
|
|
73
|
+
"playback-error",
|
|
74
|
+
"playback-track-changed",
|
|
75
|
+
|
|
76
|
+
"remote-play-pause",
|
|
77
|
+
"remote-stop",
|
|
78
|
+
"remote-pause",
|
|
79
|
+
"remote-play",
|
|
80
|
+
"remote-duck",
|
|
81
|
+
"remote-next",
|
|
82
|
+
"remote-seek",
|
|
83
|
+
"remote-previous",
|
|
84
|
+
"remote-jump-forward",
|
|
85
|
+
"remote-jump-backward",
|
|
86
|
+
"remote-like",
|
|
87
|
+
"remote-dislike",
|
|
88
|
+
"remote-bookmark",
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func setupInterruptionHandling() {
|
|
93
|
+
let notificationCenter = NotificationCenter.default
|
|
94
|
+
notificationCenter.removeObserver(self)
|
|
95
|
+
notificationCenter.addObserver(self,
|
|
96
|
+
selector: #selector(handleInterruption),
|
|
97
|
+
name: AVAudioSession.interruptionNotification,
|
|
98
|
+
object: nil)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@objc func handleInterruption(notification: Notification) {
|
|
102
|
+
guard let userInfo = notification.userInfo,
|
|
103
|
+
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
|
|
104
|
+
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let center = MPNowPlayingInfoCenter.default()
|
|
109
|
+
|
|
110
|
+
if(center.nowPlayingInfo == nil){
|
|
111
|
+
center.nowPlayingInfo = [
|
|
112
|
+
MPMediaItemPropertyTitle: "",
|
|
113
|
+
MPMediaItemPropertyArtist: "",
|
|
114
|
+
MPMediaItemPropertyAlbumTitle: "",
|
|
115
|
+
MPMediaItemPropertyPlaybackDuration: 0,
|
|
116
|
+
MPNowPlayingInfoPropertyElapsedPlaybackTime: 0,
|
|
117
|
+
MPNowPlayingInfoPropertyPlaybackRate: 0
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if type == .began {
|
|
122
|
+
|
|
123
|
+
var wasSupended = userInfo[AVAudioSessionInterruptionWasSuspendedKey] as? Bool
|
|
124
|
+
|
|
125
|
+
#if TARGET_OS_IOS
|
|
126
|
+
if #available(iOS 14.5, *) {
|
|
127
|
+
let reason = userInfo[AVAudioSessionInterruptionReasonKey] as? NSNumber
|
|
128
|
+
|
|
129
|
+
if(reason != nil && reason == 1){
|
|
130
|
+
wasSupended = true
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
#endif
|
|
134
|
+
|
|
135
|
+
if(wasSupended != nil && wasSupended == true){
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
center.nowPlayingInfo![MPNowPlayingInfoPropertyPlaybackRate] = 0
|
|
140
|
+
// Interruption began, take appropriate actions
|
|
141
|
+
self.sendEvent(withName: "remote-duck", body: [
|
|
142
|
+
"paused": true
|
|
143
|
+
])
|
|
144
|
+
}
|
|
145
|
+
else if type == .ended {
|
|
146
|
+
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
|
|
147
|
+
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
|
|
148
|
+
if options.contains(.shouldResume) {
|
|
149
|
+
// Interruption Ended - playback should resume
|
|
150
|
+
center.nowPlayingInfo![MPNowPlayingInfoPropertyPlaybackRate] = 1.0
|
|
151
|
+
self.sendEvent(withName: "remote-duck", body: [
|
|
152
|
+
"paused": false
|
|
153
|
+
])
|
|
154
|
+
} else {
|
|
155
|
+
// Interruption Ended - playback should NOT resume
|
|
156
|
+
self.sendEvent(withName: "remote-duck", body: [
|
|
157
|
+
"paused": true,
|
|
158
|
+
"permanent": true
|
|
159
|
+
])
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private func setupPlayer() {
|
|
166
|
+
if hasInitialized {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
setupInterruptionHandling();
|
|
171
|
+
|
|
172
|
+
let center = MPRemoteCommandCenter.shared()
|
|
173
|
+
|
|
174
|
+
if #available(iOS 9.1, *) {
|
|
175
|
+
center.changePlaybackPositionCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
176
|
+
if let event = commandEvent as? MPChangePlaybackPositionCommandEvent {
|
|
177
|
+
self.sendEvent(withName: "remote-seek", body: ["position": event.positionTime])
|
|
178
|
+
return MPRemoteCommandHandlerStatus.success
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return MPRemoteCommandHandlerStatus.commandFailed
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
center.playCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
186
|
+
self.sendEvent(withName: "remote-play", body: nil)
|
|
187
|
+
return MPRemoteCommandHandlerStatus.success
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
center.pauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
192
|
+
self.sendEvent(withName: "remote-pause", body: nil)
|
|
193
|
+
return MPRemoteCommandHandlerStatus.success
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
center.nextTrackCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
197
|
+
self.sendEvent(withName: "remote-next", body: nil)
|
|
198
|
+
return MPRemoteCommandHandlerStatus.success
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
center.previousTrackCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
202
|
+
self.sendEvent(withName: "remote-previous", body: nil)
|
|
203
|
+
return MPRemoteCommandHandlerStatus.success
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
center.skipBackwardCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
207
|
+
if let command = commandEvent.command as? MPSkipIntervalCommand,
|
|
208
|
+
let interval = command.preferredIntervals.first {
|
|
209
|
+
self.sendEvent(withName: "remote-jump-backward", body: ["interval": interval])
|
|
210
|
+
return MPRemoteCommandHandlerStatus.success
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return MPRemoteCommandHandlerStatus.commandFailed
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
center.skipForwardCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
217
|
+
if let command = commandEvent.command as? MPSkipIntervalCommand,
|
|
218
|
+
let interval = command.preferredIntervals.first {
|
|
219
|
+
self.sendEvent(withName: "remote-jump-forward", body: ["interval": interval])
|
|
220
|
+
return MPRemoteCommandHandlerStatus.success
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return MPRemoteCommandHandlerStatus.commandFailed
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
center.stopCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
227
|
+
self.sendEvent(withName: "remote-stop", body: nil)
|
|
228
|
+
return MPRemoteCommandHandlerStatus.success
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
center.togglePlayPauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
232
|
+
self.sendEvent(withName: "remote-play-pause", body: nil)
|
|
233
|
+
return MPRemoteCommandHandlerStatus.success
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
center.likeCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
238
|
+
self.sendEvent(withName: "remote-like", body: nil)
|
|
239
|
+
return MPRemoteCommandHandlerStatus.success
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
center.dislikeCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
243
|
+
self.sendEvent(withName: "remote-dislike", body: nil)
|
|
244
|
+
return MPRemoteCommandHandlerStatus.success
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
center.bookmarkCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
|
|
248
|
+
self.sendEvent(withName: "remote-bookmark", body: nil)
|
|
249
|
+
return MPRemoteCommandHandlerStatus.success
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
hasInitialized = true
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@objc(reset:rejecter:)
|
|
256
|
+
public func reset(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
257
|
+
print("Resetting player.")
|
|
258
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
|
|
259
|
+
resolve(NSNull())
|
|
260
|
+
DispatchQueue.main.async {
|
|
261
|
+
UIApplication.shared.endReceivingRemoteControlEvents();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@objc(updateOptions:resolver:rejecter:)
|
|
266
|
+
public func update(options: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
267
|
+
DispatchQueue.main.async {
|
|
268
|
+
let capabilitiesStr = options["capabilities"] as? [String]
|
|
269
|
+
let capabilities = capabilitiesStr?.compactMap { Capability(rawValue: $0) } ?? []
|
|
270
|
+
|
|
271
|
+
let jumpInterval = options["jumpInterval"] as? NSNumber
|
|
272
|
+
let likeOptions = options["likeOptions"] as? [String: Any]
|
|
273
|
+
let dislikeOptions = options["dislikeOptions"] as? [String: Any]
|
|
274
|
+
let bookmarkOptions = options["bookmarkOptions"] as? [String: Any]
|
|
275
|
+
|
|
276
|
+
let center = MPRemoteCommandCenter.shared()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
if #available(iOS 9.1, *) {
|
|
280
|
+
center.changePlaybackPositionCommand.isEnabled = capabilities.contains(.seek)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
center.togglePlayPauseCommand.isEnabled = capabilities.contains(.play)
|
|
284
|
+
|
|
285
|
+
center.playCommand.isEnabled = capabilities.contains(.play)
|
|
286
|
+
center.pauseCommand.isEnabled = capabilities.contains(.pause)
|
|
287
|
+
center.nextTrackCommand.isEnabled = capabilities.contains(.next)
|
|
288
|
+
center.previousTrackCommand.isEnabled = capabilities.contains(.previous)
|
|
289
|
+
|
|
290
|
+
center.skipBackwardCommand.isEnabled = capabilities.contains(.jumpBackward)
|
|
291
|
+
center.skipBackwardCommand.preferredIntervals = [jumpInterval ?? 15]
|
|
292
|
+
|
|
293
|
+
center.skipForwardCommand.isEnabled = capabilities.contains(.jumpForward)
|
|
294
|
+
center.skipForwardCommand.preferredIntervals = [jumpInterval ?? 15]
|
|
295
|
+
|
|
296
|
+
center.stopCommand.isEnabled = capabilities.contains(.stop)
|
|
297
|
+
|
|
298
|
+
center.likeCommand.isEnabled = likeOptions?["isActive"] as? Bool ?? false//capabilities.contains(.like)
|
|
299
|
+
center.likeCommand.localizedTitle = likeOptions?["title"] as? String ?? "Like"
|
|
300
|
+
center.likeCommand.localizedShortTitle = likeOptions?["title"] as? String ?? "Like"
|
|
301
|
+
|
|
302
|
+
center.dislikeCommand.isEnabled = dislikeOptions?["isActive"] as? Bool ?? false//capabilities.contains(.like)
|
|
303
|
+
center.dislikeCommand.localizedTitle = dislikeOptions?["title"] as? String ?? "Dislike"
|
|
304
|
+
center.dislikeCommand.localizedShortTitle = dislikeOptions?["title"] as? String ?? "Dislike"
|
|
305
|
+
|
|
306
|
+
center.bookmarkCommand.isEnabled = bookmarkOptions?["isActive"] as? Bool ?? false//capabilities.contains(.like)
|
|
307
|
+
center.bookmarkCommand.localizedTitle = bookmarkOptions?["title"] as? String ?? "Bookmark"
|
|
308
|
+
center.bookmarkCommand.localizedShortTitle = bookmarkOptions?["title"] as? String ?? "Bookmark"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
//load placeholder
|
|
312
|
+
if(self.placeHolderImageArtwork == nil && options["placeholderImage"] != nil){
|
|
313
|
+
let placeHolderImage : UIImage = RCTConvert.uiImage(options["placeholderImage"])
|
|
314
|
+
|
|
315
|
+
if #available(iOS 10.0, *) {
|
|
316
|
+
self.placeHolderImageArtwork = MPMediaItemArtwork.init(boundsSize: placeHolderImage.size, requestHandler: { (size) -> UIImage in
|
|
317
|
+
return placeHolderImage
|
|
318
|
+
})
|
|
319
|
+
} else {
|
|
320
|
+
self.placeHolderImageArtwork = MPMediaItemArtwork(image: placeHolderImage)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
resolve(NSNull())
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@objc(setNowPlaying:resolver:rejecter:)
|
|
330
|
+
public func setNowPlaying(trackDict: [String: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
331
|
+
|
|
332
|
+
if(!hasInitialized){
|
|
333
|
+
setupPlayer()
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
337
|
+
UIApplication.shared.beginReceivingRemoteControlEvents();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
currentTrack = Track(dictionary: trackDict)
|
|
341
|
+
updatePlayback(properties: trackDict, resolve: resolve, reject: reject)
|
|
342
|
+
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@objc(updatePlayback:resolver:rejecter:)
|
|
346
|
+
public func updatePlayback(properties: [String: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
347
|
+
|
|
348
|
+
let center = MPNowPlayingInfoCenter.default()
|
|
349
|
+
|
|
350
|
+
let stateRaw = properties["state"] as? String
|
|
351
|
+
|
|
352
|
+
let state = stateRaw != nil ? PlayState(rawValue: stateRaw!) : PlayState.none
|
|
353
|
+
|
|
354
|
+
currentTrack?.updateMetadata(dictionary: properties)
|
|
355
|
+
|
|
356
|
+
updateMetadata(properties: properties, state: state)
|
|
357
|
+
|
|
358
|
+
let commandCenter = MPRemoteCommandCenter.shared()
|
|
359
|
+
|
|
360
|
+
if(state == PlayState.stopped){
|
|
361
|
+
commandCenter.stopCommand.isEnabled = false
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#if TARGET_OS_IOS
|
|
365
|
+
if #available(iOS 13.0, *) {
|
|
366
|
+
if (state == PlayState.playing) {
|
|
367
|
+
center.playbackState = MPNowPlayingPlaybackState.playing
|
|
368
|
+
} else if (state == PlayState.paused) {
|
|
369
|
+
center.playbackState = MPNowPlayingPlaybackState.paused;
|
|
370
|
+
} else if (state == PlayState.stopped) {
|
|
371
|
+
center.playbackState = MPNowPlayingPlaybackState.stopped;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
#endif
|
|
375
|
+
|
|
376
|
+
resolve(NSNull())
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private func updateMetadata(properties: [String: Any], state: PlayState!) {
|
|
380
|
+
|
|
381
|
+
let center = MPNowPlayingInfoCenter.default()
|
|
382
|
+
|
|
383
|
+
if(center.nowPlayingInfo == nil){
|
|
384
|
+
center.nowPlayingInfo = [
|
|
385
|
+
MPMediaItemPropertyTitle: "",
|
|
386
|
+
MPMediaItemPropertyArtist: "",
|
|
387
|
+
MPMediaItemPropertyAlbumTitle: "",
|
|
388
|
+
MPMediaItemPropertyPlaybackDuration: 0,
|
|
389
|
+
MPNowPlayingInfoPropertyElapsedPlaybackTime: 0,
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
let elapsedTime = properties["elapsedTime"] as? Double
|
|
395
|
+
|
|
396
|
+
var newNowPlaying = center.nowPlayingInfo
|
|
397
|
+
|
|
398
|
+
newNowPlaying![MPMediaItemPropertyTitle] = currentTrack?.title ?? center.nowPlayingInfo![MPMediaItemPropertyTitle]
|
|
399
|
+
newNowPlaying![MPMediaItemPropertyArtist] = currentTrack?.artist ?? center.nowPlayingInfo![MPMediaItemPropertyArtist]
|
|
400
|
+
newNowPlaying![MPMediaItemPropertyAlbumTitle] = currentTrack?.album ?? center.nowPlayingInfo![MPMediaItemPropertyAlbumTitle]
|
|
401
|
+
newNowPlaying![MPMediaItemPropertyPlaybackDuration] = currentTrack?.duration ?? center.nowPlayingInfo![MPMediaItemPropertyPlaybackDuration]
|
|
402
|
+
newNowPlaying![MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTime ?? center.nowPlayingInfo![MPNowPlayingInfoPropertyElapsedPlaybackTime]
|
|
403
|
+
newNowPlaying![MPNowPlayingInfoPropertyPlaybackRate] = state == PlayState.paused ? 0 : 1.0
|
|
404
|
+
|
|
405
|
+
let newArtworkUrl = properties["artwork"] as? String
|
|
406
|
+
|
|
407
|
+
self.artworkUrl = MediaURL(object: newArtworkUrl)
|
|
408
|
+
|
|
409
|
+
//add placeholder while image is loading
|
|
410
|
+
if(newArtworkUrl != nil && newArtworkUrl != self.previousArtworkUrl /*&& !(self.artworkUrl?.isLocal ?? false)*/){
|
|
411
|
+
newNowPlaying![MPMediaItemPropertyArtwork] = placeHolderImageArtwork
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = newNowPlaying
|
|
415
|
+
|
|
416
|
+
//updateArtworkIfNeeded(artworkUrl: newArtworkUrl, newNowPlaying: newNowPlaying!)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
if(newArtworkUrl == nil){
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if(self.previousArtworkUrl == newArtworkUrl && newNowPlaying![MPMediaItemPropertyArtwork] != nil){
|
|
424
|
+
return
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if(newArtworkUrl == ""){
|
|
428
|
+
return
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
self.previousArtworkUrl = newArtworkUrl
|
|
432
|
+
|
|
433
|
+
self.getArtwork { [weak self] image in
|
|
434
|
+
if let image = image {
|
|
435
|
+
|
|
436
|
+
// check whether image is loaded
|
|
437
|
+
if (image.cgImage == nil && image.ciImage == nil) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if(self?.previousArtworkUrl != newArtworkUrl){
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
let artwork = self?.mediaItemArtwork(from: image)//MPMediaItemArtwork(from: image)
|
|
447
|
+
|
|
448
|
+
if(MPNowPlayingInfoCenter.default().nowPlayingInfo != nil)
|
|
449
|
+
{
|
|
450
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo![MPMediaItemPropertyArtwork] = artwork
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
func getArtwork(_ handler: @escaping (UIImage?) -> Void) {
|
|
457
|
+
if let artworkURL = self.artworkUrl?.value {
|
|
458
|
+
if(self.artworkUrl?.isLocal ?? false){
|
|
459
|
+
|
|
460
|
+
if(FileManager.default.fileExists(atPath: artworkURL.path)){
|
|
461
|
+
let image = UIImage.init(named: artworkURL.path);
|
|
462
|
+
handler(image);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
} else {
|
|
466
|
+
URLSession.shared.dataTask(with: artworkURL, completionHandler: { (data, _, error) in
|
|
467
|
+
if let data = data, let artwork = UIImage(data: data), error == nil {
|
|
468
|
+
handler(artwork)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
handler(nil)
|
|
472
|
+
}).resume()
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
handler(nil)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
fileprivate func mediaItemArtwork(from image: UIImage) -> MPMediaItemArtwork {
|
|
480
|
+
if #available(iOS 10.0, *) {
|
|
481
|
+
return MPMediaItemArtwork.init(boundsSize: image.size, requestHandler: { (size: CGSize) -> UIImage in
|
|
482
|
+
return image
|
|
483
|
+
})
|
|
484
|
+
} else {
|
|
485
|
+
return MPMediaItemArtwork(image: image)
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RNTrackPlayerBridge.m
|
|
3
|
+
// RNTrackPlayerBridge
|
|
4
|
+
//
|
|
5
|
+
// Created by David Chavez on 7/1/17.
|
|
6
|
+
// Copyright © 2017 David Chavez. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import "RNTrackPlayerBridge.h"
|
|
10
|
+
#import <React/RCTBridgeModule.h>
|
|
11
|
+
#import <React/RCTConvert.h>
|
|
12
|
+
|
|
13
|
+
@interface RCT_EXTERN_REMAP_MODULE(TrackPlayerModule, RNTrackPlayer, NSObject)
|
|
14
|
+
|
|
15
|
+
RCT_EXTERN_METHOD(updateOptions:(NSDictionary *)options
|
|
16
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
17
|
+
rejecter:(RCTPromiseRejectBlock)reject);
|
|
18
|
+
|
|
19
|
+
RCT_EXTERN_METHOD(setNowPlaying:(NSDictionary *)trackDict
|
|
20
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
21
|
+
rejecter:(RCTPromiseRejectBlock)reject);
|
|
22
|
+
|
|
23
|
+
RCT_EXTERN_METHOD(updatePlayback:(NSDictionary *)properties
|
|
24
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
25
|
+
rejecter:(RCTPromiseRejectBlock)reject);
|
|
26
|
+
|
|
27
|
+
RCT_EXTERN_METHOD(reset:(RCTPromiseResolveBlock)resolve
|
|
28
|
+
rejecter:(RCTPromiseRejectBlock)reject);
|
|
29
|
+
@end
|