react-native-mp3-player 1.0.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 (328) hide show
  1. package/LICENSE +205 -0
  2. package/README.md +88 -0
  3. package/android/build.gradle +114 -0
  4. package/android/proguard-rules.txt +0 -0
  5. package/android/src/main/AndroidManifest.xml +27 -0
  6. package/android/src/main/java/com/doublesymmetry/trackplayer/HeadlessJsMediaService.kt +199 -0
  7. package/android/src/main/java/com/doublesymmetry/trackplayer/TrackPlayer.kt +30 -0
  8. package/android/src/main/java/com/doublesymmetry/trackplayer/extensions/AudioPlayerStateExt.kt +19 -0
  9. package/android/src/main/java/com/doublesymmetry/trackplayer/extensions/EnumExtensions.kt +5 -0
  10. package/android/src/main/java/com/doublesymmetry/trackplayer/extensions/NumberExt.kt +13 -0
  11. package/android/src/main/java/com/doublesymmetry/trackplayer/model/MetadataAdapter.kt +227 -0
  12. package/android/src/main/java/com/doublesymmetry/trackplayer/model/NowPlayingMetadata.kt +16 -0
  13. package/android/src/main/java/com/doublesymmetry/trackplayer/model/PlaybackMetadata.kt +203 -0
  14. package/android/src/main/java/com/doublesymmetry/trackplayer/model/State.kt +13 -0
  15. package/android/src/main/java/com/doublesymmetry/trackplayer/model/Track.kt +67 -0
  16. package/android/src/main/java/com/doublesymmetry/trackplayer/model/TrackAudioItem.kt +18 -0
  17. package/android/src/main/java/com/doublesymmetry/trackplayer/model/TrackMetadata.kt +38 -0
  18. package/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicEvents.kt +65 -0
  19. package/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt +775 -0
  20. package/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt +1251 -0
  21. package/android/src/main/java/com/doublesymmetry/trackplayer/utils/AppForegroundTracker.kt +35 -0
  22. package/android/src/main/java/com/doublesymmetry/trackplayer/utils/BundleUtils.kt +147 -0
  23. package/android/src/main/java/com/doublesymmetry/trackplayer/utils/CoilBitmapLoader.kt +64 -0
  24. package/android/src/main/java/com/doublesymmetry/trackplayer/utils/MediaItemBuilder.kt +41 -0
  25. package/android/src/main/java/com/doublesymmetry/trackplayer/utils/RejectionException.kt +11 -0
  26. package/android/src/main/java/com/lovegaoshi/kotlinaudio/event/EventHolder.kt +30 -0
  27. package/android/src/main/java/com/lovegaoshi/kotlinaudio/event/PlayerEventHolder.kt +124 -0
  28. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/AudioContentType.kt +10 -0
  29. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/AudioItem.kt +133 -0
  30. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/AudioItemTransitionReason.kt +33 -0
  31. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/AudioPlayerState.kt +30 -0
  32. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/BufferConfig.kt +8 -0
  33. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/CacheConfig.kt +17 -0
  34. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/Capability.kt +19 -0
  35. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/CustomButton.kt +19 -0
  36. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/FocusChangeData.kt +3 -0
  37. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/MediaSessionCallback.kt +17 -0
  38. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PlayWhenReadyChangeData.kt +3 -0
  39. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PlaybackEndedReason.kt +5 -0
  40. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PlaybackError.kt +6 -0
  41. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PlayerConfig.kt +36 -0
  42. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PlayerOptions.kt +39 -0
  43. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/PositionChangedReason.kt +39 -0
  44. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/RepeatMode.kt +16 -0
  45. package/android/src/main/java/com/lovegaoshi/kotlinaudio/models/WakeMode.kt +7 -0
  46. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/AudioPlayer.kt +689 -0
  47. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/ForwardingPlayer.java +1124 -0
  48. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/QueuedAudioPlayer.kt +295 -0
  49. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/components/Buffer.kt +34 -0
  50. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/components/Cache.kt +47 -0
  51. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/components/FocusManager.kt +59 -0
  52. package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/components/MediaFactory.kt +165 -0
  53. package/android/src/main/java/com/lovegaoshi/kotlinaudio/service/MusicService.kt +127 -0
  54. package/android/src/main/java/com/lovegaoshi/kotlinaudio/utils/Utils.kt +113 -0
  55. package/android/src/main/res/drawable/baseline_repeat_24.xml +5 -0
  56. package/android/src/main/res/drawable/baseline_repeat_one_24.xml +5 -0
  57. package/android/src/main/res/drawable/forward.xml +5 -0
  58. package/android/src/main/res/drawable/heart_24px.xml +5 -0
  59. package/android/src/main/res/drawable/hearte_24px.xml +5 -0
  60. package/android/src/main/res/drawable/ifl_24px.xml +5 -0
  61. package/android/src/main/res/drawable/rewind.xml +5 -0
  62. package/android/src/main/res/drawable/shuffle_24px.xml +5 -0
  63. package/android/src/main/res/values/strings.xml +5 -0
  64. package/android/src/main/res/xml/automotive_app_desc.xml +3 -0
  65. package/ios/Example/SwiftAudio/Assets.xcassets/22AMI.imageset/22AMillion.jpg +0 -0
  66. package/ios/Example/SwiftAudio/Assets.xcassets/22AMI.imageset/Contents.json +21 -0
  67. package/ios/Example/SwiftAudio/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  68. package/ios/Example/SwiftAudio/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
  69. package/ios/Example/SwiftAudio/Assets.xcassets/Contents.json +6 -0
  70. package/ios/Example/SwiftAudio/Assets.xcassets/cover.imageset/Contents.json +21 -0
  71. package/ios/Example/SwiftAudio/Assets.xcassets/cover.imageset/cover.jpg +0 -0
  72. package/ios/Example/SwiftAudio/AudioController.swift +46 -0
  73. package/ios/Example/SwiftAudio/Extensions.swift +22 -0
  74. package/ios/Example/SwiftAudio/PlayerView.swift +172 -0
  75. package/ios/Example/SwiftAudio/PlayerViewModel.swift +120 -0
  76. package/ios/Example/SwiftAudio/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
  77. package/ios/Example/SwiftAudio/QueueView.swift +65 -0
  78. package/ios/Example/SwiftAudio/SwiftAudio.entitlements +12 -0
  79. package/ios/Example/SwiftAudio/SwiftAudioApp.swift +17 -0
  80. package/ios/Example/SwiftAudio.xcodeproj/project.pbxproj +412 -0
  81. package/ios/Example/SwiftAudio.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  82. package/ios/Example/SwiftAudio.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  83. package/ios/RNTrackPlayer/Models/Capabilities.swift +52 -0
  84. package/ios/RNTrackPlayer/Models/MediaURL.swift +38 -0
  85. package/ios/RNTrackPlayer/Models/MetadataAdapter.swift +147 -0
  86. package/ios/RNTrackPlayer/Models/PitchAlgorithms.swift +13 -0
  87. package/ios/RNTrackPlayer/Models/SessionCategories.swift +106 -0
  88. package/ios/RNTrackPlayer/Models/State.swift +26 -0
  89. package/ios/RNTrackPlayer/Models/Track.swift +140 -0
  90. package/ios/RNTrackPlayer/RNTrackPlayer.swift +941 -0
  91. package/ios/RNTrackPlayer/Support/RNTrackPlayer-Bridging-Header.h +7 -0
  92. package/ios/RNTrackPlayer/TrackPlayer.h +14 -0
  93. package/ios/RNTrackPlayer/TrackPlayer.mm +246 -0
  94. package/ios/RNTrackPlayer/Utils/EventType.swift +44 -0
  95. package/ios/RNTrackPlayer/Utils/Metadata.swift +60 -0
  96. package/ios/SwiftAudioEx/Package.swift +20 -0
  97. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AVPlayerWrapper/AVPlayerWrapper.swift +521 -0
  98. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AVPlayerWrapper/AVPlayerWrapperDelegate.swift +27 -0
  99. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AVPlayerWrapper/AVPlayerWrapperProtocol.swift +69 -0
  100. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AVPlayerWrapper/AVPlayerWrapperState.swift +43 -0
  101. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioItem.swift +158 -0
  102. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioPlayer.swift +459 -0
  103. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioPlayerError.swift +26 -0
  104. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioSessionController/AudioSession.swift +33 -0
  105. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioSessionController/AudioSessionController.swift +135 -0
  106. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AudioTap.swift +99 -0
  107. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Event.swift +155 -0
  108. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/MediaItemProperty.swift +95 -0
  109. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/NowPlayingInfoCenter.swift +17 -0
  110. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/NowPlayingInfoController.swift +73 -0
  111. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/NowPlayingInfoControllerProtocol.swift +26 -0
  112. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/NowPlayingInfoKeyValue.swift +14 -0
  113. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/NowPlayingInfoController/NowPlayingInfoProperty.swift +234 -0
  114. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Observer/AVPlayerItemNotificationObserver.swift +102 -0
  115. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Observer/AVPlayerItemObserver.swift +136 -0
  116. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Observer/AVPlayerObserver.swift +120 -0
  117. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Observer/AVPlayerTimeObserver.swift +112 -0
  118. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/QueueManager.swift +356 -0
  119. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/QueuedAudioPlayer.swift +236 -0
  120. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/RemoteCommandController/RemoteCommand.swift +170 -0
  121. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/RemoteCommandController/RemoteCommandController.swift +206 -0
  122. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/RepeatMode.swift +15 -0
  123. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/TimeEventFrequency.swift +26 -0
  124. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/Utils/DispatchQueueType.swift +18 -0
  125. package/ios/SwiftAudioEx/Sources/SwiftAudioEx/WaveformAudioTap.swift +159 -0
  126. package/lib/specs/NativeTrackPlayer.d.ts +129 -0
  127. package/lib/specs/NativeTrackPlayer.js +4 -0
  128. package/lib/src/constants/AndroidAudioContentType.d.ts +35 -0
  129. package/lib/src/constants/AndroidAudioContentType.js +36 -0
  130. package/lib/src/constants/AndroidAutoContentStyle.d.ts +10 -0
  131. package/lib/src/constants/AndroidAutoContentStyle.js +11 -0
  132. package/lib/src/constants/AppKilledPlaybackBehavior.d.ts +17 -0
  133. package/lib/src/constants/AppKilledPlaybackBehavior.js +18 -0
  134. package/lib/src/constants/Capability.d.ts +17 -0
  135. package/lib/src/constants/Capability.js +19 -0
  136. package/lib/src/constants/Event.d.ts +163 -0
  137. package/lib/src/constants/Event.js +164 -0
  138. package/lib/src/constants/IOSCategory.d.ts +36 -0
  139. package/lib/src/constants/IOSCategory.js +37 -0
  140. package/lib/src/constants/IOSCategoryMode.d.ts +47 -0
  141. package/lib/src/constants/IOSCategoryMode.js +48 -0
  142. package/lib/src/constants/IOSCategoryOptions.d.ts +44 -0
  143. package/lib/src/constants/IOSCategoryOptions.js +45 -0
  144. package/lib/src/constants/MediaItemPlayable.d.ts +4 -0
  145. package/lib/src/constants/MediaItemPlayable.js +5 -0
  146. package/lib/src/constants/PitchAlgorithm.d.ts +14 -0
  147. package/lib/src/constants/PitchAlgorithm.js +16 -0
  148. package/lib/src/constants/RatingType.d.ts +8 -0
  149. package/lib/src/constants/RatingType.js +10 -0
  150. package/lib/src/constants/RepeatMode.d.ts +8 -0
  151. package/lib/src/constants/RepeatMode.js +10 -0
  152. package/lib/src/constants/State.d.ts +34 -0
  153. package/lib/src/constants/State.js +35 -0
  154. package/lib/src/constants/TrackType.d.ts +6 -0
  155. package/lib/src/constants/TrackType.js +7 -0
  156. package/lib/src/constants/index.d.ts +14 -0
  157. package/lib/src/constants/index.js +14 -0
  158. package/lib/src/hooks/index.d.ts +6 -0
  159. package/lib/src/hooks/index.js +6 -0
  160. package/lib/src/hooks/useActiveTrack.d.ts +2 -0
  161. package/lib/src/hooks/useActiveTrack.js +28 -0
  162. package/lib/src/hooks/useAppIsInBackground.d.ts +1 -0
  163. package/lib/src/hooks/useAppIsInBackground.js +16 -0
  164. package/lib/src/hooks/useIsPlaying.d.ts +35 -0
  165. package/lib/src/hooks/useIsPlaying.js +50 -0
  166. package/lib/src/hooks/usePlayWhenReady.d.ts +1 -0
  167. package/lib/src/hooks/usePlayWhenReady.js +27 -0
  168. package/lib/src/hooks/usePlaybackState.d.ts +10 -0
  169. package/lib/src/hooks/usePlaybackState.js +35 -0
  170. package/lib/src/hooks/useProgress.d.ts +7 -0
  171. package/lib/src/hooks/useProgress.js +55 -0
  172. package/lib/src/hooks/useTrackPlayerEvents.d.ts +8 -0
  173. package/lib/src/hooks/useTrackPlayerEvents.js +30 -0
  174. package/lib/src/index.d.ts +5 -0
  175. package/lib/src/index.js +5 -0
  176. package/lib/src/interfaces/AndroidAutoBrowseTree.d.ts +5 -0
  177. package/lib/src/interfaces/AndroidAutoBrowseTree.js +1 -0
  178. package/lib/src/interfaces/AndroidOptions.d.ts +41 -0
  179. package/lib/src/interfaces/AndroidOptions.js +1 -0
  180. package/lib/src/interfaces/CustomButtons.d.ts +5 -0
  181. package/lib/src/interfaces/CustomButtons.js +1 -0
  182. package/lib/src/interfaces/FeedbackOptions.d.ts +6 -0
  183. package/lib/src/interfaces/FeedbackOptions.js +1 -0
  184. package/lib/src/interfaces/MediaItem.d.ts +18 -0
  185. package/lib/src/interfaces/MediaItem.js +1 -0
  186. package/lib/src/interfaces/MetadataOptions.d.ts +3 -0
  187. package/lib/src/interfaces/MetadataOptions.js +1 -0
  188. package/lib/src/interfaces/NowPlayingMetadata.d.ts +4 -0
  189. package/lib/src/interfaces/NowPlayingMetadata.js +1 -0
  190. package/lib/src/interfaces/PlaybackState.d.ts +8 -0
  191. package/lib/src/interfaces/PlaybackState.js +1 -0
  192. package/lib/src/interfaces/PlayerOptions.d.ts +127 -0
  193. package/lib/src/interfaces/PlayerOptions.js +1 -0
  194. package/lib/src/interfaces/Progress.d.ts +15 -0
  195. package/lib/src/interfaces/Progress.js +1 -0
  196. package/lib/src/interfaces/ResourceObject.d.ts +1 -0
  197. package/lib/src/interfaces/ResourceObject.js +1 -0
  198. package/lib/src/interfaces/ServiceHandler.d.ts +1 -0
  199. package/lib/src/interfaces/ServiceHandler.js +1 -0
  200. package/lib/src/interfaces/Track.d.ts +21 -0
  201. package/lib/src/interfaces/Track.js +1 -0
  202. package/lib/src/interfaces/TrackMetadataBase.d.ts +28 -0
  203. package/lib/src/interfaces/TrackMetadataBase.js +1 -0
  204. package/lib/src/interfaces/UpdateOptions.d.ts +52 -0
  205. package/lib/src/interfaces/UpdateOptions.js +1 -0
  206. package/lib/src/interfaces/events/AudioMetadataReceivedEvent.d.ts +33 -0
  207. package/lib/src/interfaces/events/AudioMetadataReceivedEvent.js +1 -0
  208. package/lib/src/interfaces/events/ControllerConnectedEvent.d.ts +8 -0
  209. package/lib/src/interfaces/events/ControllerConnectedEvent.js +1 -0
  210. package/lib/src/interfaces/events/EventPayloadByEvent.d.ts +73 -0
  211. package/lib/src/interfaces/events/EventPayloadByEvent.js +1 -0
  212. package/lib/src/interfaces/events/PlaybackActiveTrackChangedEvent.d.ts +24 -0
  213. package/lib/src/interfaces/events/PlaybackActiveTrackChangedEvent.js +1 -0
  214. package/lib/src/interfaces/events/PlaybackAnimatedVolumeChangedEvent.d.ts +4 -0
  215. package/lib/src/interfaces/events/PlaybackAnimatedVolumeChangedEvent.js +1 -0
  216. package/lib/src/interfaces/events/PlaybackErrorEvent.d.ts +6 -0
  217. package/lib/src/interfaces/events/PlaybackErrorEvent.js +1 -0
  218. package/lib/src/interfaces/events/PlaybackMetadataReceivedEvent.d.ts +16 -0
  219. package/lib/src/interfaces/events/PlaybackMetadataReceivedEvent.js +1 -0
  220. package/lib/src/interfaces/events/PlaybackPlayWhenReadyChangedEvent.d.ts +4 -0
  221. package/lib/src/interfaces/events/PlaybackPlayWhenReadyChangedEvent.js +1 -0
  222. package/lib/src/interfaces/events/PlaybackProgressUpdatedEvent.d.ts +4 -0
  223. package/lib/src/interfaces/events/PlaybackProgressUpdatedEvent.js +1 -0
  224. package/lib/src/interfaces/events/PlaybackQueueEndedEvent.d.ts +9 -0
  225. package/lib/src/interfaces/events/PlaybackQueueEndedEvent.js +1 -0
  226. package/lib/src/interfaces/events/PlaybackResumeEvent.d.ts +3 -0
  227. package/lib/src/interfaces/events/PlaybackResumeEvent.js +1 -0
  228. package/lib/src/interfaces/events/PlaybackTrackChangedEvent.d.ts +11 -0
  229. package/lib/src/interfaces/events/PlaybackTrackChangedEvent.js +1 -0
  230. package/lib/src/interfaces/events/PlayerErrorEvent.d.ts +6 -0
  231. package/lib/src/interfaces/events/PlayerErrorEvent.js +1 -0
  232. package/lib/src/interfaces/events/RemoteBrowseEvent.d.ts +4 -0
  233. package/lib/src/interfaces/events/RemoteBrowseEvent.js +1 -0
  234. package/lib/src/interfaces/events/RemoteCustomActionEvent.d.ts +7 -0
  235. package/lib/src/interfaces/events/RemoteCustomActionEvent.js +1 -0
  236. package/lib/src/interfaces/events/RemoteDuckEvent.d.ts +13 -0
  237. package/lib/src/interfaces/events/RemoteDuckEvent.js +1 -0
  238. package/lib/src/interfaces/events/RemoteJumpBackwardEvent.d.ts +8 -0
  239. package/lib/src/interfaces/events/RemoteJumpBackwardEvent.js +1 -0
  240. package/lib/src/interfaces/events/RemoteJumpForwardEvent.d.ts +8 -0
  241. package/lib/src/interfaces/events/RemoteJumpForwardEvent.js +1 -0
  242. package/lib/src/interfaces/events/RemotePlayIdEvent.d.ts +4 -0
  243. package/lib/src/interfaces/events/RemotePlayIdEvent.js +1 -0
  244. package/lib/src/interfaces/events/RemotePlaySearchEvent.d.ts +9 -0
  245. package/lib/src/interfaces/events/RemotePlaySearchEvent.js +1 -0
  246. package/lib/src/interfaces/events/RemoteSeekEvent.d.ts +4 -0
  247. package/lib/src/interfaces/events/RemoteSeekEvent.js +1 -0
  248. package/lib/src/interfaces/events/RemoteSetRatingEvent.d.ts +4 -0
  249. package/lib/src/interfaces/events/RemoteSetRatingEvent.js +1 -0
  250. package/lib/src/interfaces/events/RemoteSkipEvent.d.ts +3 -0
  251. package/lib/src/interfaces/events/RemoteSkipEvent.js +1 -0
  252. package/lib/src/interfaces/events/index.d.ts +18 -0
  253. package/lib/src/interfaces/events/index.js +18 -0
  254. package/lib/src/interfaces/index.d.ts +15 -0
  255. package/lib/src/interfaces/index.js +15 -0
  256. package/lib/src/resolveAssetSource.d.ts +2 -0
  257. package/lib/src/resolveAssetSource.js +3 -0
  258. package/lib/src/trackPlayer.d.ts +347 -0
  259. package/lib/src/trackPlayer.js +592 -0
  260. package/package.json +94 -0
  261. package/react-native-mp3-player.podspec +22 -0
  262. package/specs/NativeTrackPlayer.ts +148 -0
  263. package/src/constants/AndroidAudioContentType.ts +35 -0
  264. package/src/constants/AndroidAutoContentStyle.ts +10 -0
  265. package/src/constants/AppKilledPlaybackBehavior.ts +19 -0
  266. package/src/constants/Capability.ts +19 -0
  267. package/src/constants/Event.ts +164 -0
  268. package/src/constants/IOSCategory.ts +36 -0
  269. package/src/constants/IOSCategoryMode.ts +47 -0
  270. package/src/constants/IOSCategoryOptions.ts +44 -0
  271. package/src/constants/MediaItemPlayable.ts +4 -0
  272. package/src/constants/PitchAlgorithm.ts +16 -0
  273. package/src/constants/RatingType.ts +10 -0
  274. package/src/constants/RepeatMode.ts +10 -0
  275. package/src/constants/State.ts +34 -0
  276. package/src/constants/TrackType.ts +6 -0
  277. package/src/constants/index.ts +14 -0
  278. package/src/hooks/index.ts +6 -0
  279. package/src/hooks/useActiveTrack.ts +36 -0
  280. package/src/hooks/useAppIsInBackground.ts +20 -0
  281. package/src/hooks/useIsPlaying.ts +56 -0
  282. package/src/hooks/usePlayWhenReady.ts +37 -0
  283. package/src/hooks/usePlaybackState.ts +45 -0
  284. package/src/hooks/useProgress.ts +64 -0
  285. package/src/hooks/useTrackPlayerEvents.ts +48 -0
  286. package/src/index.ts +7 -0
  287. package/src/interfaces/AndroidAutoBrowseTree.ts +6 -0
  288. package/src/interfaces/AndroidOptions.ts +48 -0
  289. package/src/interfaces/CustomButtons.ts +6 -0
  290. package/src/interfaces/FeedbackOptions.ts +7 -0
  291. package/src/interfaces/MediaItem.ts +19 -0
  292. package/src/interfaces/MetadataOptions.ts +4 -0
  293. package/src/interfaces/NowPlayingMetadata.ts +5 -0
  294. package/src/interfaces/PlaybackState.ts +11 -0
  295. package/src/interfaces/PlayerOptions.ts +133 -0
  296. package/src/interfaces/Progress.ts +15 -0
  297. package/src/interfaces/ResourceObject.ts +1 -0
  298. package/src/interfaces/ServiceHandler.ts +1 -0
  299. package/src/interfaces/Track.ts +23 -0
  300. package/src/interfaces/TrackMetadataBase.ts +29 -0
  301. package/src/interfaces/UpdateOptions.ts +59 -0
  302. package/src/interfaces/events/AudioMetadataReceivedEvent.ts +37 -0
  303. package/src/interfaces/events/ControllerConnectedEvent.ts +9 -0
  304. package/src/interfaces/events/EventPayloadByEvent.ts +76 -0
  305. package/src/interfaces/events/PlaybackActiveTrackChangedEvent.ts +29 -0
  306. package/src/interfaces/events/PlaybackAnimatedVolumeChangedEvent.ts +4 -0
  307. package/src/interfaces/events/PlaybackErrorEvent.ts +6 -0
  308. package/src/interfaces/events/PlaybackMetadataReceivedEvent.ts +16 -0
  309. package/src/interfaces/events/PlaybackPlayWhenReadyChangedEvent.ts +4 -0
  310. package/src/interfaces/events/PlaybackProgressUpdatedEvent.ts +5 -0
  311. package/src/interfaces/events/PlaybackQueueEndedEvent.ts +9 -0
  312. package/src/interfaces/events/PlaybackResumeEvent.ts +5 -0
  313. package/src/interfaces/events/PlaybackTrackChangedEvent.ts +11 -0
  314. package/src/interfaces/events/PlayerErrorEvent.ts +6 -0
  315. package/src/interfaces/events/RemoteBrowseEvent.ts +4 -0
  316. package/src/interfaces/events/RemoteCustomActionEvent.ts +7 -0
  317. package/src/interfaces/events/RemoteDuckEvent.ts +13 -0
  318. package/src/interfaces/events/RemoteJumpBackwardEvent.ts +8 -0
  319. package/src/interfaces/events/RemoteJumpForwardEvent.ts +8 -0
  320. package/src/interfaces/events/RemotePlayIdEvent.ts +4 -0
  321. package/src/interfaces/events/RemotePlaySearchEvent.ts +21 -0
  322. package/src/interfaces/events/RemoteSeekEvent.ts +4 -0
  323. package/src/interfaces/events/RemoteSetRatingEvent.ts +5 -0
  324. package/src/interfaces/events/RemoteSkipEvent.ts +3 -0
  325. package/src/interfaces/events/index.ts +18 -0
  326. package/src/interfaces/index.ts +15 -0
  327. package/src/resolveAssetSource.ts +3 -0
  328. package/src/trackPlayer.ts +768 -0
@@ -0,0 +1,775 @@
1
+ package com.doublesymmetry.trackplayer.module
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.*
5
+ import android.os.Build
6
+ import android.os.Bundle
7
+ import android.os.IBinder
8
+ import android.support.v4.media.RatingCompat
9
+ import androidx.media3.common.MediaItem
10
+ import androidx.media.utils.MediaConstants
11
+ import com.lovegaoshi.kotlinaudio.models.Capability
12
+ import com.lovegaoshi.kotlinaudio.models.RepeatMode
13
+ import com.doublesymmetry.trackplayer.model.State
14
+ import com.doublesymmetry.trackplayer.model.Track
15
+ import com.doublesymmetry.trackplayer.module.MusicEvents.Companion.EVENT_INTENT
16
+ import com.doublesymmetry.trackplayer.service.MusicService
17
+ import com.doublesymmetry.trackplayer.utils.AppForegroundTracker
18
+ import com.doublesymmetry.trackplayer.utils.RejectionException
19
+ import com.doublesymmetry.trackplayer.NativeTrackPlayerSpec
20
+ import com.facebook.react.bridge.*
21
+ import androidx.media3.common.Player
22
+ import androidx.media3.session.MediaBrowser
23
+ import androidx.media3.session.SessionToken
24
+ import com.doublesymmetry.trackplayer.utils.buildMediaItem
25
+ import kotlinx.coroutines.MainScope
26
+ import kotlinx.coroutines.delay
27
+ import kotlinx.coroutines.launch
28
+ import kotlinx.coroutines.withContext
29
+ import androidx.core.net.toUri
30
+ import kotlinx.coroutines.Dispatchers
31
+ import timber.log.Timber
32
+ import java.util.*
33
+ import javax.annotation.Nonnull
34
+ import androidx.core.net.toUri
35
+ import com.facebook.react.module.annotations.ReactModule
36
+ import com.lovegaoshi.kotlinaudio.models.AudioPlayerState
37
+
38
+
39
+ /**
40
+ * @author Milen Pivchev @mpivchev
41
+ */
42
+ @ReactModule(name = MusicModule.NAME)
43
+ class MusicModule(reactContext: ReactApplicationContext) : NativeTrackPlayerSpec(reactContext),
44
+ ServiceConnection {
45
+ private lateinit var browser: MediaBrowser
46
+ private var playerOptions: Bundle? = null
47
+ private var isServiceBound = false
48
+ private var playerSetUpPromise: Promise? = null
49
+ private val scope = MainScope()
50
+ private lateinit var musicService: MusicService
51
+ private val context = reactContext
52
+
53
+ override fun getName() = NAME
54
+
55
+ companion object {
56
+ const val NAME = "TrackPlayer"
57
+ }
58
+
59
+ override fun addListener(eventType: String) {
60
+ // No implementation needed for TurboModule
61
+ // This implements the abstract method required by NativeTrackPlayerSpec
62
+ }
63
+
64
+ override fun removeListeners(count: Double) {
65
+ // No implementation needed for TurboModule
66
+ // This implements the abstract method required by NativeTrackPlayerSpec
67
+ }
68
+
69
+ override fun initialize() {
70
+ AppForegroundTracker.start()
71
+ }
72
+
73
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
74
+ launchInScope {
75
+ // If a binder already exists, don't get a new one
76
+ if (!::musicService.isInitialized) {
77
+ val binder: MusicService.MusicBinder = service as MusicService.MusicBinder
78
+ musicService = binder.service
79
+ musicService.setupPlayer(playerOptions)
80
+ playerSetUpPromise?.resolve(null)
81
+ }
82
+
83
+ isServiceBound = true
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Called when a connection to the Service has been lost.
89
+ */
90
+ override fun onServiceDisconnected(name: ComponentName) {
91
+ launchInScope {
92
+ isServiceBound = false
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Checks wither service is bound, or rejects. Returns whether promise was rejected.
98
+ */
99
+ private fun verifyServiceBoundOrReject(promise: Promise): Boolean {
100
+ if (!isServiceBound) {
101
+ promise.reject(
102
+ "player_not_initialized",
103
+ "The player is not initialized. Call setupPlayer first."
104
+ )
105
+ return true
106
+ }
107
+
108
+ return false
109
+ }
110
+
111
+ private fun bundleToTrack(bundle: Bundle): Track {
112
+ return Track(context, bundle, 0)
113
+ }
114
+
115
+ private fun hashmapToMediaItem(hashmap: HashMap<String, String>): MediaItem {
116
+ val mediaUri = hashmap["mediaUri"]
117
+ val iconUri = hashmap["iconUri"]
118
+
119
+ val extras = Bundle()
120
+ hashmap["groupTitle"]?.let {
121
+ extras.putString(
122
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, it)
123
+ }
124
+ hashmap["contentStyle"]?.toInt()?.let {
125
+ extras.putInt(
126
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, it)
127
+ }
128
+ hashmap["childrenPlayableContentStyle"]?.toInt()?.let {
129
+ extras.putInt(
130
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, it)
131
+ }
132
+ hashmap["childrenBrowsableContentStyle"]?.toInt()?.let {
133
+ extras.putInt(
134
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, it)
135
+ }
136
+
137
+ // playbackProgress should contain a string representation of a number between 0 and 1 if present
138
+ hashmap["playbackProgress"]?.toDouble()?.let {
139
+ if (it > 0.98) {
140
+ extras.putInt(
141
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
142
+ MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED)
143
+ } else if (it == 0.0) {
144
+ extras.putInt(
145
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
146
+ MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED)
147
+ } else {
148
+ extras.putInt(
149
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
150
+ MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED)
151
+ extras.putDouble(
152
+ MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, it)
153
+ }
154
+ }
155
+ return buildMediaItem(
156
+ isPlayable = hashmap["playable"]?.toInt() != 1,
157
+ title = hashmap["title"],
158
+ mediaId = hashmap["mediaId"] ?: "no-media-id",
159
+ imageUri = iconUri?.toUri(),
160
+ artist = hashmap["subtitle"],
161
+ subtitle = hashmap["subtitle"],
162
+ sourceUri = mediaUri?.toUri(),
163
+ extras = extras
164
+ )
165
+ }
166
+
167
+ private fun readableArrayToMediaItems(data: ArrayList<HashMap<String, String>>): MutableList<MediaItem> {
168
+ return data.map {
169
+ hashmapToMediaItem(it)
170
+ }.toMutableList()
171
+ }
172
+
173
+ private fun rejectWithException(callback: Promise, exception: Exception) {
174
+ when (exception) {
175
+ is RejectionException -> {
176
+ callback.reject(exception.code, exception)
177
+ }
178
+ else -> {
179
+ callback.reject("runtime_exception", exception)
180
+ }
181
+ }
182
+ }
183
+
184
+ private fun readableArrayToTrackList(data: ReadableArray?): MutableList<Track> {
185
+ val bundleList = Arguments.toList(data)
186
+ if (bundleList !is ArrayList) {
187
+ throw RejectionException("invalid_parameter", "Was not given an array of tracks")
188
+ }
189
+ return bundleList.map {
190
+ if (it is Bundle) {
191
+ bundleToTrack(it)
192
+ } else {
193
+ throw RejectionException(
194
+ "invalid_track_object",
195
+ "Track was not a dictionary type"
196
+ )
197
+ }
198
+ }.toMutableList()
199
+ }
200
+
201
+ /* ****************************** API ****************************** */
202
+ override fun getTypedExportedConstants(): MutableMap<String, Any> {
203
+ return HashMap<String, Any>().apply {
204
+ // Capabilities
205
+ this["CAPABILITY_PLAY"] = Capability.PLAY.ordinal
206
+ this["CAPABILITY_PLAY_FROM_ID"] = Capability.PLAY_FROM_ID.ordinal
207
+ this["CAPABILITY_PLAY_FROM_SEARCH"] = Capability.PLAY_FROM_SEARCH.ordinal
208
+ this["CAPABILITY_PAUSE"] = Capability.PAUSE.ordinal
209
+ this["CAPABILITY_STOP"] = Capability.STOP.ordinal
210
+ this["CAPABILITY_SEEK_TO"] = Capability.SEEK_TO.ordinal
211
+ this["CAPABILITY_SKIP"] = OnErrorAction.SKIP.ordinal
212
+ this["CAPABILITY_SKIP_TO_NEXT"] = Capability.SKIP_TO_NEXT.ordinal
213
+ this["CAPABILITY_SKIP_TO_PREVIOUS"] = Capability.SKIP_TO_PREVIOUS.ordinal
214
+ this["CAPABILITY_SET_RATING"] = Capability.SET_RATING.ordinal
215
+ this["CAPABILITY_JUMP_FORWARD"] = Capability.JUMP_FORWARD.ordinal
216
+ this["CAPABILITY_JUMP_BACKWARD"] = Capability.JUMP_BACKWARD.ordinal
217
+
218
+ // States
219
+ this["STATE_NONE"] = State.None.state
220
+ this["STATE_READY"] = State.Ready.state
221
+ this["STATE_PLAYING"] = State.Playing.state
222
+ this["STATE_PAUSED"] = State.Paused.state
223
+ this["STATE_STOPPED"] = State.Stopped.state
224
+ this["STATE_BUFFERING"] = State.Buffering.state
225
+ this["STATE_LOADING"] = State.Loading.state
226
+
227
+ // Rating Types
228
+ this["RATING_HEART"] = RatingCompat.RATING_HEART
229
+ this["RATING_THUMBS_UP_DOWN"] = RatingCompat.RATING_THUMB_UP_DOWN
230
+ this["RATING_3_STARS"] = RatingCompat.RATING_3_STARS
231
+ this["RATING_4_STARS"] = RatingCompat.RATING_4_STARS
232
+ this["RATING_5_STARS"] = RatingCompat.RATING_5_STARS
233
+ this["RATING_PERCENTAGE"] = RatingCompat.RATING_PERCENTAGE
234
+
235
+ // Repeat Modes
236
+ this["REPEAT_OFF"] = Player.REPEAT_MODE_OFF
237
+ this["REPEAT_TRACK"] = Player.REPEAT_MODE_ONE
238
+ this["REPEAT_QUEUE"] = Player.REPEAT_MODE_ALL
239
+
240
+ // TODO: not implemented
241
+ this["PITCH_ALGORITHM_LINEAR"] = -1
242
+ this["PITCH_ALGORITHM_MUSIC"] = -1
243
+ this["PITCH_ALGORITHM_VOICE"] = -1
244
+ this["CAPABILITY_LIKE"] = -1
245
+ this["CAPABILITY_DISLIKE"] = -1
246
+ this["CAPABILITY_BOOKMARK"] = -1
247
+
248
+ }
249
+ }
250
+
251
+ @SuppressLint("UnspecifiedRegisterReceiverFlag")
252
+ override fun setupPlayer(options: ReadableMap?, background: Boolean, promise: Promise) {
253
+ if (isServiceBound) {
254
+ promise.reject(
255
+ "player_already_initialized",
256
+ "The player has already been initialized via setupPlayer."
257
+ )
258
+ return
259
+ }
260
+
261
+ // prevent crash Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException
262
+ if (!background
263
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
264
+ && AppForegroundTracker.backgrounded) {
265
+ promise.reject(
266
+ "android_cannot_setup_player_in_background",
267
+ "On Android the app must be in the foreground when setting up the player."
268
+ )
269
+ return
270
+ }
271
+
272
+
273
+ val bundledData = Arguments.toBundle(options)
274
+
275
+ playerSetUpPromise = promise
276
+ playerOptions = bundledData
277
+
278
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
279
+ context.registerReceiver(
280
+ MusicEvents(context),
281
+ IntentFilter(EVENT_INTENT), Context.RECEIVER_NOT_EXPORTED
282
+ )
283
+ } else {
284
+ context.registerReceiver(
285
+ MusicEvents(context),
286
+ IntentFilter(EVENT_INTENT)
287
+ )
288
+ }
289
+
290
+ val musicModule = this
291
+ try {
292
+ Intent(context, MusicService::class.java).also { intent ->
293
+ context.bindService(intent, musicModule, Context.BIND_AUTO_CREATE)
294
+ val sessionToken =
295
+ SessionToken(context, ComponentName(context, MusicService::class.java))
296
+ val browserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()
297
+ // browser = browserFuture.get()
298
+ }
299
+ } catch (exception: Exception) {
300
+ Timber.tag("RNTP").w(exception, "Could not initialize service")
301
+ throw exception
302
+ }
303
+ }
304
+
305
+ override fun updateOptions(data: ReadableMap?, callback: Promise) = launchInScope {
306
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
307
+
308
+ val options = Arguments.toBundle(data)
309
+
310
+ options?.let {
311
+ musicService.updateOptions(it)
312
+ }
313
+
314
+ callback.resolve(null)
315
+ }
316
+
317
+ override fun add(data: ReadableArray?, insertBeforeIndex: Double, callback: Promise) = launchInScope {
318
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
319
+
320
+ val insertB4Index = insertBeforeIndex.toInt()
321
+
322
+ try {
323
+ val tracks = readableArrayToTrackList(data)
324
+ if (insertB4Index < -1 || insertB4Index > musicService.tracks.size) {
325
+ callback.reject("index_out_of_bounds", "The track index is out of bounds")
326
+ return@launchInScope
327
+ }
328
+ val index = if (insertB4Index == -1) musicService.tracks.size else insertB4Index
329
+ musicService.add(
330
+ tracks,
331
+ index
332
+ )
333
+ callback.resolve(index)
334
+ } catch (exception: Exception) {
335
+ rejectWithException(callback, exception)
336
+ }
337
+ }
338
+
339
+ override fun load(data: ReadableMap?, callback: Promise) = launchInScope {
340
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
341
+ if (data == null) {
342
+ callback.resolve(null)
343
+ return@launchInScope
344
+ }
345
+ val bundle = Arguments.toBundle(data)
346
+ if (bundle is Bundle) {
347
+ musicService.load(bundleToTrack(bundle))
348
+ callback.resolve(null)
349
+ } else {
350
+ callback.reject("invalid_track_object", "Track was not a dictionary type")
351
+ }
352
+ }
353
+
354
+ override fun move(fromIndex: Double, toIndex: Double, callback: Promise) = launchInScope {
355
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
356
+ musicService.move(fromIndex.toInt(), toIndex.toInt())
357
+ callback.resolve(null)
358
+ }
359
+
360
+ override fun remove(data: ReadableArray?, callback: Promise) = launchInScope {
361
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
362
+ val inputIndexes = Arguments.toList(data)
363
+ if (inputIndexes != null) {
364
+ val size = musicService.tracks.size
365
+ val indexes: ArrayList<Int> = ArrayList()
366
+ for (inputIndex in inputIndexes) {
367
+ val index = inputIndex as? Int ?: inputIndex.toString().toInt()
368
+ if (index < 0 || index >= size) {
369
+ callback.reject(
370
+ "index_out_of_bounds",
371
+ "One or more indexes was out of bounds"
372
+ )
373
+ return@launchInScope
374
+ }
375
+ indexes.add(index)
376
+ }
377
+ musicService.remove(indexes)
378
+ }
379
+ callback.resolve(null)
380
+ }
381
+
382
+ override fun updateMetadataForTrack(index: Double, map: ReadableMap?, callback: Promise) =
383
+ launchInScope {
384
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
385
+
386
+ val indexInt = index.toInt()
387
+ if (indexInt < 0 || indexInt >= musicService.tracks.size) {
388
+ callback.reject("index_out_of_bounds", "The index is out of bounds")
389
+ } else {
390
+ val context: ReactContext = context
391
+ val track = musicService.tracks[indexInt]
392
+ track.setMetadata(context, Arguments.toBundle(map), 0)
393
+ musicService.updateMetadataForTrack(indexInt, track)
394
+
395
+ callback.resolve(null)
396
+ }
397
+ }
398
+
399
+ override fun updateNowPlayingMetadata(map: ReadableMap?, callback: Promise) = launchInScope {
400
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
401
+
402
+ if (musicService.tracks.isEmpty())
403
+ callback.reject("no_current_item", "There is no current item in the player")
404
+
405
+ Arguments.toBundle(map)?.let {
406
+ val track = bundleToTrack(it)
407
+ musicService.updateNowPlayingMetadata(track)
408
+ }
409
+
410
+ callback.resolve(null)
411
+ }
412
+
413
+ override fun removeUpcomingTracks(callback: Promise) = launchInScope {
414
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
415
+
416
+ musicService.removeUpcomingTracks()
417
+ callback.resolve(null)
418
+ }
419
+
420
+ override fun skip(index: Double, initialPosition: Double, callback: Promise) = launchInScope {
421
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
422
+ musicService.skip(index.toInt())
423
+ if (initialPosition >= 0) {
424
+ musicService.seekTo(initialPosition.toFloat())
425
+ }
426
+
427
+ callback.resolve(null)
428
+ }
429
+
430
+ override fun skipToNext(initialTime: Double, callback: Promise) = launchInScope {
431
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
432
+
433
+ musicService.skipToNext()
434
+
435
+ if (initialTime >= 0) {
436
+ musicService.seekTo(initialTime.toFloat())
437
+ }
438
+
439
+ callback.resolve(null)
440
+ }
441
+
442
+ override fun skipToPrevious(initialTime: Double, callback: Promise) = launchInScope {
443
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
444
+
445
+ musicService.skipToPrevious()
446
+
447
+ if (initialTime >= 0) {
448
+ musicService.seekTo(initialTime.toFloat())
449
+ }
450
+
451
+ callback.resolve(null)
452
+ }
453
+
454
+ override fun reset(callback: Promise) = launchInScope {
455
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
456
+
457
+ musicService.stop()
458
+ delay(300) // Allow playback to stop
459
+ musicService.clear()
460
+
461
+ callback.resolve(null)
462
+ }
463
+
464
+ override fun play(callback: Promise) = launchInScope {
465
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
466
+
467
+ musicService.play()
468
+ callback.resolve(null)
469
+ }
470
+
471
+ override fun pause(callback: Promise) = launchInScope {
472
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
473
+
474
+ musicService.pause()
475
+ callback.resolve(null)
476
+ }
477
+
478
+ override fun stop(callback: Promise) = launchInScope {
479
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
480
+
481
+ musicService.stop()
482
+ callback.resolve(null)
483
+ }
484
+
485
+ override fun seekTo(seconds: Double, callback: Promise) = launchInScope {
486
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
487
+
488
+ musicService.seekTo(seconds.toFloat())
489
+ callback.resolve(null)
490
+ }
491
+
492
+ override fun seekBy(offset: Double, callback: Promise) = launchInScope {
493
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
494
+
495
+ musicService.seekBy(offset.toFloat())
496
+ callback.resolve(null)
497
+ }
498
+
499
+ override fun retry(callback: Promise) = launchInScope {
500
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
501
+
502
+ musicService.retry()
503
+ callback.resolve(null)
504
+ }
505
+
506
+ override fun setVolume(volume: Double, callback: Promise) = launchInScope {
507
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
508
+
509
+ musicService.setVolume(volume.toFloat())
510
+ callback.resolve(null)
511
+ }
512
+
513
+ override fun getVolume(callback: Promise) = launchInScope {
514
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
515
+
516
+ callback.resolve(musicService.getVolume())
517
+ }
518
+
519
+ override fun setRate(rate: Double, callback: Promise) = launchInScope {
520
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
521
+
522
+ musicService.setRate(rate.toFloat())
523
+ callback.resolve(null)
524
+ }
525
+
526
+ override fun getPitch(callback: Promise) = launchInScope {
527
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
528
+
529
+ callback.resolve(musicService.getPitch())
530
+ }
531
+
532
+ override fun setPitch(rate: Double, callback: Promise) = launchInScope {
533
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
534
+
535
+ musicService.setPitch(rate.toFloat())
536
+ callback.resolve(null)
537
+ }
538
+
539
+ override fun getRate(callback: Promise) = launchInScope {
540
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
541
+
542
+ callback.resolve(musicService.getRate())
543
+ }
544
+
545
+ override fun setRepeatMode(mode: Double, callback: Promise) = launchInScope {
546
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
547
+
548
+ musicService.setRepeatMode(RepeatMode.fromOrdinal(mode.toInt()))
549
+ callback.resolve(null)
550
+ }
551
+
552
+ override fun getRepeatMode(callback: Promise) = launchInScope {
553
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
554
+
555
+ callback.resolve(musicService.getRepeatMode().ordinal)
556
+ }
557
+
558
+ override fun setPlayWhenReady(playWhenReady: Boolean, callback: Promise) = launchInScope {
559
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
560
+
561
+ musicService.playWhenReady = playWhenReady
562
+ callback.resolve(null)
563
+ }
564
+
565
+ override fun getPlayWhenReady(callback: Promise) = launchInScope {
566
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
567
+
568
+ callback.resolve(musicService.playWhenReady)
569
+ }
570
+
571
+ override fun getTrack(index: Double, callback: Promise) = launchInScope {
572
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
573
+
574
+ val indexInt = index.toInt()
575
+ if (indexInt >= 0 && indexInt < musicService.tracks.size) {
576
+ val originalItem = musicService.tracks[indexInt].originalItem
577
+ callback.resolve(if (originalItem != null) Arguments.fromBundle(originalItem) else null)
578
+ } else {
579
+ callback.resolve(null)
580
+ }
581
+ }
582
+
583
+ override fun getQueue(callback: Promise) = launchInScope {
584
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
585
+
586
+ callback.resolve(Arguments.fromList(musicService.tracks.map { it.originalItem }))
587
+ }
588
+
589
+ override fun setQueue(data: ReadableArray?, callback: Promise) = launchInScope {
590
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
591
+
592
+ try {
593
+ Timber.tag("RNTP").d("setQueue: Starting - clearing current queue")
594
+
595
+ // Ensure we're on the main thread for MusicService operations
596
+ withContext(Dispatchers.Main) {
597
+ musicService.clear()
598
+
599
+ Timber.tag("RNTP").d("setQueue: Converting tracks from ReadableArray")
600
+ val tracks = readableArrayToTrackList(data)
601
+ Timber.tag("RNTP").d("setQueue: Adding ${tracks.size} tracks to queue")
602
+
603
+ musicService.add(tracks)
604
+ }
605
+
606
+ Timber.tag("RNTP").d("setQueue: Successfully added tracks, resolving promise")
607
+ callback.resolve(null)
608
+ } catch (exception: Exception) {
609
+ Timber.tag("RNTP").e(exception, "setQueue: Error occurred")
610
+ rejectWithException(callback, exception)
611
+ }
612
+ }
613
+
614
+ override fun getActiveTrackIndex(callback: Promise) = launchInScope {
615
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
616
+ callback.resolve(
617
+ if (musicService.tracks.isEmpty()) null else musicService.getCurrentTrackIndex()
618
+ )
619
+ }
620
+
621
+ override fun getActiveTrack(callback: Promise) = launchInScope {
622
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
623
+ if(musicService.tracks.isEmpty()) {
624
+ callback.resolve(null)
625
+ return@launchInScope
626
+ }
627
+
628
+ val activeTrack = musicService.tracks[musicService.getCurrentTrackIndex()].originalItem
629
+ callback.resolve(if(activeTrack == null) null else Arguments.fromBundle(activeTrack))
630
+ }
631
+
632
+
633
+ override fun getProgress(callback: Promise) = launchInScope {
634
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
635
+ val bundle = Bundle()
636
+ bundle.putDouble("duration", musicService.getDurationInSeconds())
637
+ bundle.putDouble("position", musicService.getPositionInSeconds())
638
+ bundle.putDouble("buffered", musicService.getBufferedPositionInSeconds())
639
+ callback.resolve(Arguments.fromBundle(bundle))
640
+ }
641
+
642
+ override fun getPlaybackState(callback: Promise) = launchInScope {
643
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
644
+ callback.resolve(Arguments.fromBundle(musicService.getPlayerStateBundle(musicService.state)))
645
+ }
646
+
647
+ override fun setAnimatedVolume(volume: Double, duration: Double, interval: Double, msg: String, callback: Promise) = launchInScope {
648
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
649
+ musicService.setAnimatedVolume(volume.toFloat(), duration.toLong(), interval.toLong(), msg).await()
650
+ delay(duration.toLong())
651
+ callback.resolve(null)
652
+ }
653
+
654
+ override fun fadeOutPause(duration: Double, interval: Double, callback: Promise) = launchInScope {
655
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
656
+ musicService.fadeOutPause(duration.toLong(), interval.toLong())
657
+ delay(duration.toLong())
658
+ callback.resolve(null)
659
+ }
660
+
661
+ override fun fadeOutNext(duration: Double, interval: Double, toVolume: Double, callback: Promise) = launchInScope {
662
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
663
+ musicService.fadeOutNext(duration.toLong(), interval.toLong(), toVolume.toFloat())
664
+ delay(duration.toLong())
665
+ callback.resolve(null)
666
+ }
667
+
668
+ override fun fadeOutPrevious(duration: Double, interval: Double, toVolume: Double, callback: Promise) = launchInScope {
669
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
670
+ musicService.fadeOutPrevious(duration.toLong(), interval.toLong(), toVolume.toFloat())
671
+ delay(duration.toLong())
672
+ callback.resolve(null)
673
+ }
674
+
675
+ override fun fadeOutJump(
676
+ index: Double,
677
+ duration: Double,
678
+ interval: Double,
679
+ toVolume: Double,
680
+ callback: Promise
681
+ ) = launchInScope {
682
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
683
+ musicService.fadeOutJump(index.toInt(), duration.toLong(), interval.toLong(), toVolume.toFloat())
684
+ delay(duration.toLong())
685
+ callback.resolve(null)
686
+ }
687
+
688
+ override fun setBrowseTree(mediaItems: ReadableMap, callback: Promise) = launchInScope {
689
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
690
+ val mediaItemsMap = mediaItems.toHashMap()
691
+ musicService.mediaTree = mediaItemsMap.mapValues { readableArrayToMediaItems(it.value as ArrayList<HashMap<String, String>>) }
692
+ Timber.tag("APM").d("refreshing browseTree")
693
+ musicService.notifyChildrenChanged()
694
+ callback.resolve(musicService.mediaTree.toString())
695
+ }
696
+
697
+ // this method doesn't seem to affect style after onGetRoot is called, and won't change if notifyChildrenChanged is emitted.
698
+ override fun setBrowseTreeStyle(
699
+ browsableStyle: Double,
700
+ playableStyle: Double,
701
+ callback: Promise
702
+ ) = launchInScope {
703
+ fun getStyle(check: Int): Int {
704
+ return when (check) {
705
+ 1 -> MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
706
+ 2 -> MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
707
+ 3 -> MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
708
+ else -> MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
709
+ }
710
+ }
711
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
712
+ musicService.mediaTreeStyle = listOf(
713
+ getStyle(browsableStyle.toInt()),
714
+ getStyle(playableStyle.toInt())
715
+ )
716
+ callback.resolve(null)
717
+ }
718
+
719
+ override fun setPlaybackState(mediaID: String, callback: Promise) = launchInScope {
720
+ // TODO: not implemented!
721
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
722
+ callback.resolve(null)
723
+ }
724
+
725
+ override fun acquireWakeLock(callback: Promise) = launchInScope {
726
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
727
+ musicService.acquireWakeLock()
728
+ callback.resolve(null)
729
+ }
730
+
731
+ override fun abandonWakeLock(callback: Promise) = launchInScope {
732
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
733
+ musicService.abandonWakeLock()
734
+ callback.resolve(null)
735
+ }
736
+
737
+ override fun crossFadePrepare(previous: Boolean, seektoDouble: Double?, callback: Promise) = launchInScope {
738
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
739
+ musicService.crossFadePrepare(previous, seektoDouble ?: 0.0)
740
+ callback.resolve(null)
741
+ }
742
+
743
+ override fun switchExoPlayer(
744
+ fadeDuration: Double,
745
+ fadeInterval: Double,
746
+ fadeToVolume: Double,
747
+ waitUntil: Double?,
748
+ callback: Promise) = launchInScope {
749
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
750
+ musicService.switchExoPlayer(
751
+ fadeDuration = fadeDuration.toLong(),
752
+ fadeInterval = fadeInterval.toLong(),
753
+ fadeToVolume = fadeToVolume.toFloat(),
754
+ waitUntil = waitUntil?.toLong() ?: 0
755
+ )
756
+ callback.resolve(null)
757
+ }
758
+
759
+ override fun getLastConnectedPackage(callback: Promise) = launchInScope {
760
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
761
+ callback.resolve(musicService.lastConnectedPackage)
762
+ }
763
+
764
+ fun isPlaying(): Boolean {
765
+ return musicService.state == AudioPlayerState.PLAYING
766
+ }
767
+
768
+ // Bridgeless interop layer tries to pass the `Job` from `scope.launch` to the JS side
769
+ // which causes an exception. We can work around this using a wrapper.
770
+ private fun launchInScope(block: suspend () -> Unit) {
771
+ scope.launch {
772
+ block()
773
+ }
774
+ }
775
+ }