react-native-queue-player 1.1.2 → 2.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 (367) hide show
  1. package/QueuePlayer.podspec +14 -9
  2. package/README.md +3 -3
  3. package/android/build.gradle +7 -0
  4. package/android/consumer-rules.pro +72 -3
  5. package/android/src/main/cpp/airplay2_control.c +4 -4
  6. package/android/src/main/cpp/airplay2_control.h +4 -4
  7. package/android/src/main/cpp/airplay2_jni.cpp +26 -22
  8. package/android/src/main/cpp/airplay2_pair.c +7 -7
  9. package/android/src/main/cpp/airplay2_pair.h +2 -2
  10. package/android/src/main/cpp/airplay2_rtsp.c +8 -8
  11. package/android/src/main/cpp/airplay2_session.c +2 -2
  12. package/android/src/main/cpp/airplay_jni.cpp +20 -72
  13. package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +7 -16
  14. package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +1 -1
  15. package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +2 -14
  16. package/android/src/main/java/com/margelo/nitro/queueplayer/CellularTransportMonitor.kt +77 -0
  17. package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +187 -52
  18. package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +27 -7
  19. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +33 -12
  20. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +31 -8
  21. package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +45 -30
  22. package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +111 -15
  23. package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +60 -33
  24. package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +1 -1
  25. package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +3 -1
  26. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +103 -29
  27. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +77 -29
  28. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +6 -6
  29. package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +48 -1
  30. package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +3 -2
  31. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +71 -76
  32. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +3 -3
  33. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +2 -2
  34. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +68 -46
  35. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +6 -5
  36. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +5 -6
  37. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +11 -8
  38. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +26 -6
  39. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +22 -3
  40. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +38 -12
  41. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +17 -13
  42. package/android/src/main/java/com/margelo/nitro/queueplayer/SuppliedReplayGain.kt +66 -0
  43. package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +870 -278
  44. package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +4 -8
  45. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +4 -3
  46. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastEventBridge.kt +1 -10
  47. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +17 -3
  48. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastTransportRouter.kt +19 -0
  49. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/RemotePlaybackStateMapping.kt +18 -0
  50. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +0 -2
  51. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +23 -12
  52. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +16 -9
  53. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +58 -21
  54. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +6 -14
  55. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlaySession.kt +26 -14
  56. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +1 -13
  57. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MetadataSyncTarget.kt +20 -0
  58. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +1 -1
  59. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +3 -3
  60. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastSession.kt +73 -13
  61. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +11 -35
  62. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +0 -5
  63. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +0 -6
  64. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +32 -33
  65. package/android/src/test/java/androidx/media3/session/{MediaSessionControllerRequestTestSeam.kt → MediaSessionControllerRequestTestSupport.kt} +1 -1
  66. package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +12 -0
  67. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineFocusLossTest.kt +115 -0
  68. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +45 -0
  69. package/android/src/test/java/com/margelo/nitro/queueplayer/EngineEndSignalOrderTest.kt +70 -0
  70. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +23 -12
  71. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
  72. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +43 -15
  73. package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +15 -0
  74. package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +135 -4
  75. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +1 -0
  76. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +36 -2
  77. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +24 -18
  78. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterCancelTest.kt +85 -0
  79. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +112 -1
  80. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +5 -3
  81. package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +45 -0
  82. package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +1 -1
  83. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +47 -0
  84. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +53 -29
  85. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +8 -8
  86. package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +33 -1
  87. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +128 -5
  88. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +209 -14
  89. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +150 -10
  90. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainMergeTest.kt +258 -0
  91. package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +8 -4
  92. package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +2 -2
  93. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowDynamicsProcessingRejectingEnable.kt +19 -0
  94. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizer.kt +68 -0
  95. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizerRejectingBandWrites.kt +27 -0
  96. package/android/src/test/java/com/margelo/nitro/queueplayer/SuppliedReplayGainTest.kt +61 -0
  97. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +4 -4
  98. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
  99. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastCommandRoutingTest.kt +163 -0
  100. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +123 -0
  101. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +88 -38
  102. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +205 -27
  103. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +277 -38
  104. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +5 -3
  105. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +2 -1
  106. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +85 -3
  107. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +11 -7
  108. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +2 -1
  109. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +90 -4
  110. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +8 -26
  111. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +3 -3
  112. package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +0 -4
  113. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +27 -11
  114. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSyncTest.kt +303 -0
  115. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitorTest.kt +173 -0
  116. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/ShadowConnectivityManagerRecordingRequests.kt +27 -0
  117. package/app.plugin.js +17 -1
  118. package/ios/AVPlayerItemQueueItemId.swift +26 -12
  119. package/ios/AVQueueBuilder.swift +120 -85
  120. package/ios/ArtworkLoader.swift +0 -7
  121. package/ios/ArtworkResolver.swift +22 -4
  122. package/ios/AssetReplayGainReader.swift +118 -0
  123. package/ios/AudioSession.swift +67 -16
  124. package/ios/AudioTapProvider.swift +284 -149
  125. package/ios/CarPlayBridge.swift +0 -12
  126. package/ios/CarPlayCoordinator.swift +18 -2
  127. package/ios/CarPlaySceneDelegate.swift +2 -2
  128. package/ios/Cast/AirPlayRouteState.swift +9 -8
  129. package/ios/Cast/Chromecast/ChromecastSession.swift +90 -11
  130. package/ios/Cast/Core/CastEventBridge.swift +66 -26
  131. package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
  132. package/ios/Cast/Core/CastSession.swift +11 -0
  133. package/ios/Cast/Core/CastTransportRouter.swift +21 -9
  134. package/ios/Cast/Core/LocalMediaServer.swift +41 -52
  135. package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +32 -11
  136. package/ios/Cast/Core/MediaServerHandle.swift +0 -4
  137. package/ios/Cast/Core/MediaTokenRegistry.swift +0 -6
  138. package/ios/CastManager.swift +1 -1
  139. package/ios/CrossfadeEngine.swift +660 -226
  140. package/ios/EQTap.swift +20 -17
  141. package/ios/Equalizer.swift +1 -1
  142. package/ios/FLACStreamInfo.swift +65 -0
  143. package/ios/GaplessEngine.swift +57 -30
  144. package/ios/InputGuards.swift +35 -5
  145. package/ios/LookaheadCache.swift +202 -58
  146. package/ios/LookaheadCachePrefetcher.swift +48 -36
  147. package/ios/MediaServer/MediaHTTPConnection.swift +691 -0
  148. package/ios/NetworkRecoveryPolicy.swift +32 -0
  149. package/ios/NowPlayingFormatExtractor.swift +25 -18
  150. package/ios/NowPlayingInfo.swift +84 -48
  151. package/ios/OutputRouteMonitor.swift +16 -3
  152. package/ios/PendingBrowseRequests.swift +1 -1
  153. package/ios/PlaceholderArtwork.swift +9 -5
  154. package/ios/PlaybackEngine.swift +165 -76
  155. package/ios/PlaybackErrorMapping.swift +3 -3
  156. package/ios/PlaybackModeStateMachine.swift +2 -2
  157. package/ios/PlaybackNetworkMonitor.swift +73 -0
  158. package/ios/PlayerStateDerivation.swift +6 -6
  159. package/ios/QueueMutationArithmetic.swift +25 -0
  160. package/ios/QueueSkipArithmetic.swift +5 -3
  161. package/ios/QueueWindowArithmetic.swift +152 -0
  162. package/ios/ReadThroughServer.swift +283 -0
  163. package/ios/RemoteCommands.swift +44 -4
  164. package/ios/ReplayGainData.swift +22 -3
  165. package/ios/ReplayGainExtractor.swift +70 -27
  166. package/ios/Siri/VoiceDonation.swift +14 -9
  167. package/ios/StreamingBitrateProbe.swift +22 -68
  168. package/ios/SuppliedReplayGain.swift +83 -0
  169. package/ios/Tests/AVQueueBuilderTests.swift +158 -221
  170. package/ios/Tests/ActiveItemEngineStub.swift +44 -0
  171. package/ios/Tests/AssetReplayGainReaderTests.swift +228 -0
  172. package/ios/Tests/AudioTapProviderDispatchTargetTests.swift +158 -0
  173. package/ios/Tests/AudioTapProviderReplayGainTests.swift +122 -10
  174. package/ios/Tests/BitrateReresolveBudgetTests.swift +71 -0
  175. package/ios/Tests/CastMediaItemTranslationTests.swift +72 -0
  176. package/ios/Tests/CastNowPlayingControllerTests.swift +5 -0
  177. package/ios/Tests/CrossfadeAdvanceWithoutFadeTests.swift +120 -0
  178. package/ios/Tests/CrossfadeEngineIncomingMixTests.swift +122 -0
  179. package/ios/Tests/CrossfadeEngineTests.swift +164 -0
  180. package/ios/Tests/CrossfadeMixReapplyTests.swift +66 -0
  181. package/ios/Tests/CrossfadePlaybackIntentTests.swift +81 -0
  182. package/ios/Tests/EngineSwapTests.swift +91 -0
  183. package/ios/Tests/EqualizerAudioMixProviderTests.swift +46 -0
  184. package/ios/Tests/EqualizerHybridTests.swift +5 -2
  185. package/ios/Tests/FLACStreamInfoTests.swift +88 -0
  186. package/ios/Tests/GaplessEngineLifecycleTests.swift +4 -4
  187. package/ios/Tests/InputGuardsTests.swift +41 -0
  188. package/ios/Tests/InterruptionIntentTests.swift +77 -0
  189. package/ios/Tests/LookaheadCacheCellularAccessTests.swift +185 -0
  190. package/ios/Tests/LookaheadCachePrefetcherTests.swift +53 -30
  191. package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +160 -1
  192. package/ios/Tests/LookaheadCacheTests.swift +44 -18
  193. package/ios/Tests/MediaHTTPConnectionTests.swift +79 -0
  194. package/ios/Tests/MutationDeferralTests.swift +220 -0
  195. package/ios/Tests/NowPlayingInfoTests.swift +86 -99
  196. package/ios/Tests/NowPlayingSnapshotTests.swift +127 -0
  197. package/ios/Tests/OriginRestartStitcherTests.swift +60 -0
  198. package/ios/Tests/PlaybackNetworkMonitorTests.swift +95 -0
  199. package/ios/Tests/PlaybackStateRouterTests.swift +1 -0
  200. package/ios/Tests/PlayerFixtures.swift +48 -0
  201. package/ios/Tests/PlayerStateDerivationTests.swift +10 -10
  202. package/ios/Tests/QueueMutationGenerationTests.swift +141 -0
  203. package/ios/Tests/QueueRebuildPrefixTests.swift +317 -0
  204. package/ios/Tests/QueueStateTests.swift +3 -1
  205. package/ios/Tests/QueueWindowArithmeticTests.swift +56 -0
  206. package/ios/Tests/QueueWindowSliceTests.swift +162 -0
  207. package/ios/Tests/ReadThroughRoutingLifecycleTests.swift +63 -0
  208. package/ios/Tests/ReadThroughServerTests.swift +345 -0
  209. package/ios/Tests/RemoteCommandsTests.swift +28 -0
  210. package/ios/Tests/ReplayGainExtractorTests.swift +216 -6
  211. package/ios/Tests/ReplayGainMergeTests.swift +230 -0
  212. package/ios/Tests/RetryRecoveryTests.swift +303 -0
  213. package/ios/Tests/SkipCapabilityTests.swift +233 -8
  214. package/ios/Tests/SkipIndexTests.swift +32 -2
  215. package/ios/Tests/SleepTimerPauseIntentTests.swift +43 -0
  216. package/ios/Tests/StallRecoveryTests.swift +97 -0
  217. package/ios/Tests/StreamingBitrateProbeTests.swift +17 -20
  218. package/ios/Tests/TopUpWindowGateTests.swift +394 -0
  219. package/ios/Tests/TrackPlayer+TestHops.swift +14 -0
  220. package/ios/Tests/TrackPlayerCallOrderTests.swift +93 -0
  221. package/ios/Tests/TrackPlayerCastStateTests.swift +78 -0
  222. package/ios/Tests/TrackPlayerConfigureTeardownTests.swift +86 -0
  223. package/ios/Tests/TrackPlayerEndVerdictTests.swift +165 -0
  224. package/ios/Tests/TrackPlayerSeekTests.swift +137 -0
  225. package/ios/Tests/TrackPlayerThreadingTests.swift +127 -0
  226. package/ios/Tests/TrackSourceClassifierTests.swift +33 -7
  227. package/ios/Tests/VoiceVocabularyOptInTests.swift +38 -0
  228. package/ios/TrackPlayer+Automotive.swift +101 -0
  229. package/ios/TrackPlayer+Cache.swift +232 -0
  230. package/ios/TrackPlayer+Config.swift +291 -0
  231. package/ios/TrackPlayer+EngineDelegate.swift +209 -0
  232. package/ios/TrackPlayer+EventsAPI.swift +111 -0
  233. package/ios/TrackPlayer+EventsDispatch.swift +968 -0
  234. package/ios/TrackPlayer+EventsWiring.swift +173 -0
  235. package/ios/TrackPlayer+Lifecycle.swift +930 -0
  236. package/ios/TrackPlayer+NowPlayingFormat.swift +258 -0
  237. package/ios/TrackPlayer+Queue.swift +940 -0
  238. package/ios/TrackPlayer+Recovery.swift +196 -0
  239. package/ios/TrackPlayer+Skip.swift +455 -0
  240. package/ios/TrackPlayer+SleepTimer.swift +175 -0
  241. package/ios/TrackPlayer+State.swift +108 -0
  242. package/ios/TrackPlayer+Threading.swift +135 -0
  243. package/ios/TrackPlayer+Transport.swift +267 -0
  244. package/ios/TrackPlayer+Window.swift +195 -0
  245. package/ios/TrackPlayer.swift +305 -4610
  246. package/ios/Visualizer.swift +6 -5
  247. package/ios/tests-harness/Podfile +1 -1
  248. package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +19 -11
  249. package/ios/tests-harness/scripts/seed-xcodeproj.rb +2 -2
  250. package/lib/module/hooks/useActiveTrack.js +29 -22
  251. package/lib/module/hooks/useActiveTrack.js.map +1 -1
  252. package/lib/module/hooks/useCast.js +8 -26
  253. package/lib/module/hooks/useCast.js.map +1 -1
  254. package/lib/module/hooks/useEqualizer.js +19 -12
  255. package/lib/module/hooks/useEqualizer.js.map +1 -1
  256. package/lib/module/hooks/useLookaheadCache.js +3 -7
  257. package/lib/module/hooks/useLookaheadCache.js.map +1 -1
  258. package/lib/module/hooks/useQueue.js +66 -19
  259. package/lib/module/hooks/useQueue.js.map +1 -1
  260. package/lib/module/index.js +6 -3
  261. package/lib/module/index.js.map +1 -1
  262. package/lib/module/queueDelta.js +41 -0
  263. package/lib/module/queueDelta.js.map +1 -0
  264. package/lib/module/types.js +28 -5
  265. package/lib/module/types.js.map +1 -1
  266. package/lib/typescript/TrackPlayer.nitro.d.ts +53 -28
  267. package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -1
  268. package/lib/typescript/hooks/useActiveTrack.d.ts +5 -7
  269. package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -1
  270. package/lib/typescript/hooks/useCast.d.ts +6 -1
  271. package/lib/typescript/hooks/useCast.d.ts.map +1 -1
  272. package/lib/typescript/hooks/useEqualizer.d.ts +2 -1
  273. package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -1
  274. package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -1
  275. package/lib/typescript/hooks/useQueue.d.ts +4 -4
  276. package/lib/typescript/hooks/useQueue.d.ts.map +1 -1
  277. package/lib/typescript/index.d.ts +2 -1
  278. package/lib/typescript/index.d.ts.map +1 -1
  279. package/lib/typescript/queueDelta.d.ts +10 -0
  280. package/lib/typescript/queueDelta.d.ts.map +1 -0
  281. package/lib/typescript/types.d.ts +168 -12
  282. package/lib/typescript/types.d.ts.map +1 -1
  283. package/nitrogen/generated/android/c++/JFunc_void_QueueChangeDelta_double_QueueChangeReason.hpp +85 -0
  284. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +45 -19
  285. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +3 -3
  286. package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +8 -4
  287. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
  288. package/nitrogen/generated/android/c++/JQueueChangeDelta.hpp +128 -0
  289. package/nitrogen/generated/android/c++/JTrackItem.hpp +19 -3
  290. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +0 -2
  291. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +0 -2
  292. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +0 -2
  293. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +0 -2
  294. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +0 -2
  295. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +0 -2
  296. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +0 -2
  297. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +0 -2
  298. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +0 -2
  299. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +0 -2
  300. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +0 -2
  301. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +0 -2
  302. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +0 -2
  303. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +0 -2
  304. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/{Func_void_std__vector_TrackItem__double_QueueChangeReason.kt → Func_void_QueueChangeDelta_double_QueueChangeReason.kt} +14 -16
  305. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +0 -2
  306. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +0 -2
  307. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +0 -2
  308. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +0 -2
  309. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +0 -2
  310. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +0 -2
  311. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +0 -2
  312. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +0 -2
  313. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +0 -2
  314. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +0 -2
  315. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +0 -2
  316. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +2 -0
  317. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +2 -0
  318. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +6 -4
  319. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +2 -0
  320. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +9 -4
  321. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +7 -2
  322. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/QueueChangeDelta.kt +86 -0
  323. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +24 -4
  324. package/nitrogen/generated/android/queueplayerOnLoad.cpp +2 -2
  325. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +16 -16
  326. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +80 -65
  327. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
  328. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -3
  329. package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +5 -5
  330. package/nitrogen/generated/ios/swift/Func_void_QueueChangeDelta_double_QueueChangeReason.swift +46 -0
  331. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +3 -3
  332. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +32 -24
  333. package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +20 -2
  334. package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
  335. package/nitrogen/generated/ios/swift/QueueChangeDelta.swift +82 -0
  336. package/nitrogen/generated/ios/swift/TrackItem.swift +73 -1
  337. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +6 -3
  338. package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +7 -3
  339. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
  340. package/nitrogen/generated/shared/c++/QueueChangeDelta.hpp +113 -0
  341. package/nitrogen/generated/shared/c++/TrackItem.hpp +18 -2
  342. package/package.json +8 -8
  343. package/src/TrackPlayer.nitro.ts +53 -27
  344. package/src/hooks/useActiveTrack.ts +29 -22
  345. package/src/hooks/useCast.ts +14 -15
  346. package/src/hooks/useEqualizer.ts +19 -12
  347. package/src/hooks/useLookaheadCache.ts +3 -7
  348. package/src/hooks/useQueue.ts +66 -17
  349. package/src/index.ts +13 -3
  350. package/src/queueDelta.ts +41 -0
  351. package/src/types.ts +169 -12
  352. package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +0 -82
  353. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2MetadataSync.kt +0 -189
  354. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +0 -15
  355. package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +0 -192
  356. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +0 -135
  357. package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +0 -27
  358. package/ios/Cast/Core/LocalAddressMonitor.swift +0 -110
  359. package/ios/Cast/Core/MediaHTTPConnection.swift +0 -349
  360. package/ios/MetadataReader.swift +0 -555
  361. package/ios/Tests/CrossfadeEngineStubTests.swift +0 -43
  362. package/ios/Tests/MetadataReaderTests.swift +0 -603
  363. package/ios/Tests/PlaybackEngineProtocolTests.swift +0 -92
  364. package/ios/Tests/VoiceDonationTests.swift +0 -26
  365. package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__double_QueueChangeReason.hpp +0 -101
  366. package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__double_QueueChangeReason.swift +0 -46
  367. /package/ios/{Cast/Core → MediaServer}/MimeTypes.swift +0 -0
@@ -174,14 +174,6 @@ static struct raopcl_s *toRaop(jlong handle) {
174
174
  // JNI methods
175
175
  // ---------------------------------------------------------------------------
176
176
 
177
- static jstring nativeVersion(JNIEnv *env, jclass) {
178
- return env->NewStringUTF("libraop-0.2.0");
179
- }
180
-
181
- static jboolean nativeInitSsl(JNIEnv *, jclass) {
182
- return cross_ssl_load() ? JNI_TRUE : JNI_FALSE;
183
- }
184
-
185
177
  /**
186
178
  * Open a RAOP session. Returns a handle (pointer-as-long) or 0 on failure.
187
179
  *
@@ -356,34 +348,7 @@ static jint nativeRingAvailable(JNIEnv *, jclass, jlong handle) {
356
348
  return static_cast<jint>(avail);
357
349
  }
358
350
 
359
- // -- Legacy methods (still needed for pause/flush/volume/metadata) --
360
-
361
- static jboolean nativeAcceptFrames(JNIEnv *, jclass, jlong handle) {
362
- auto *r = toRaop(handle);
363
- return (r && raopcl_accept_frames(r)) ? JNI_TRUE : JNI_FALSE;
364
- }
365
-
366
- static jboolean nativeSendChunk(JNIEnv *env, jclass,
367
- jlong handle,
368
- jbyteArray data, jint offset, jint length) {
369
- auto *r = toRaop(handle);
370
- if (!r) return JNI_FALSE;
371
-
372
- jbyte *buf = env->GetByteArrayElements(data, nullptr);
373
- if (!buf) return JNI_FALSE;
374
-
375
- uint64_t playtime = 0;
376
- // Fix: length is bytes, send_chunk expects frames (bytes / 4 for stereo S16)
377
- bool ok = raopcl_send_chunk(
378
- r,
379
- reinterpret_cast<uint8_t *>(buf + offset),
380
- static_cast<int>(length / 4),
381
- &playtime
382
- );
383
-
384
- env->ReleaseByteArrayElements(data, buf, JNI_ABORT);
385
- return ok ? JNI_TRUE : JNI_FALSE;
386
- }
351
+ // -- Transport control --
387
352
 
388
353
  static void nativePause(JNIEnv *, jclass, jlong handle) {
389
354
  auto *r = toRaop(handle);
@@ -409,6 +374,8 @@ static jboolean nativeFlush(JNIEnv *, jclass, jlong handle) {
409
374
  return (r && raopcl_flush(r)) ? JNI_TRUE : JNI_FALSE;
410
375
  }
411
376
 
377
+ // -- Volume & progress --
378
+
412
379
  static jboolean nativeSetVolume(JNIEnv *, jclass, jlong handle, jfloat vol) {
413
380
  auto *r = toRaop(handle);
414
381
  return (r && raopcl_set_volume(r, vol)) ? JNI_TRUE : JNI_FALSE;
@@ -422,41 +389,20 @@ static jboolean nativeSetProgressMs(JNIEnv *, jclass, jlong handle,
422
389
  static_cast<uint32_t>(duration))) ? JNI_TRUE : JNI_FALSE;
423
390
  }
424
391
 
425
- static jlong nativeGetProgressMs(JNIEnv *, jclass, jlong handle) {
426
- auto *r = toRaop(handle);
427
- return r ? static_cast<jlong>(raopcl_get_progress_ms(r)) : 0;
428
- }
392
+ // -- State queries --
429
393
 
430
394
  static jboolean nativeIsConnected(JNIEnv *, jclass, jlong handle) {
431
395
  auto *r = toRaop(handle);
432
396
  return (r && raopcl_is_connected(r)) ? JNI_TRUE : JNI_FALSE;
433
397
  }
434
398
 
435
- static jboolean nativeIsPlaying(JNIEnv *, jclass, jlong handle) {
436
- auto *r = toRaop(handle);
437
- return (r && raopcl_is_playing(r)) ? JNI_TRUE : JNI_FALSE;
438
- }
439
-
440
- static jint nativeGetState(JNIEnv *, jclass, jlong handle) {
441
- auto *r = toRaop(handle);
442
- return r ? static_cast<jint>(raopcl_state(r)) : 0;
443
- }
444
-
445
- static jint nativeGetLatency(JNIEnv *, jclass, jlong handle) {
446
- auto *r = toRaop(handle);
447
- return r ? static_cast<jint>(raopcl_latency(r)) : 0;
448
- }
449
-
450
- static jint nativeGetSampleRate(JNIEnv *, jclass, jlong handle) {
451
- auto *r = toRaop(handle);
452
- return r ? static_cast<jint>(raopcl_sample_rate(r)) : 0;
453
- }
454
-
455
399
  static jboolean nativeKeepalive(JNIEnv *, jclass, jlong handle) {
456
400
  auto *r = toRaop(handle);
457
401
  return (r && raopcl_keepalive(r)) ? JNI_TRUE : JNI_FALSE;
458
402
  }
459
403
 
404
+ // -- Configuration --
405
+
460
406
  static jint nativeGetLastRtspStatus(JNIEnv *, jclass, jlong handle) {
461
407
  auto *r = toRaop(handle);
462
408
  return r ? static_cast<jint>(raopcl_last_rtsp_status(r)) : 0;
@@ -484,6 +430,8 @@ static void nativeSetPassword(JNIEnv *env, jclass, jlong handle, jstring jPasswd
484
430
  }
485
431
  }
486
432
 
433
+ // -- Metadata --
434
+
487
435
  static jboolean nativeSetDaap(JNIEnv *env, jclass, jlong handle,
488
436
  jstring jTitle, jstring jArtist, jstring jAlbum) {
489
437
  auto *r = toRaop(handle);
@@ -533,8 +481,6 @@ static jboolean nativeSetArtwork(JNIEnv *env, jclass, jlong handle,
533
481
  // RegisterNatives table
534
482
  // ---------------------------------------------------------------------------
535
483
  static const JNINativeMethod sMethods[] = {
536
- {"nativeVersion", "()Ljava/lang/String;", reinterpret_cast<void *>(nativeVersion)},
537
- {"nativeInitSsl", "()Z", reinterpret_cast<void *>(nativeInitSsl)},
538
484
  {"nativeOpen", "(Ljava/lang/String;IIIIIZIIIF)J", reinterpret_cast<void *>(nativeOpen)},
539
485
  {"nativeConnect", "(JLjava/lang/String;I)Z", reinterpret_cast<void *>(nativeConnect)},
540
486
  {"nativeDisconnect", "(J)Z", reinterpret_cast<void *>(nativeDisconnect)},
@@ -542,19 +488,12 @@ static const JNINativeMethod sMethods[] = {
542
488
  {"nativeFeedPcm", "(J[BII)I", reinterpret_cast<void *>(nativeFeedPcm)},
543
489
  {"nativeBytesSent", "(J)J", reinterpret_cast<void *>(nativeBytesSent)},
544
490
  {"nativeRingAvailable", "(J)I", reinterpret_cast<void *>(nativeRingAvailable)},
545
- {"nativeAcceptFrames", "(J)Z", reinterpret_cast<void *>(nativeAcceptFrames)},
546
- {"nativeSendChunk", "(J[BII)Z", reinterpret_cast<void *>(nativeSendChunk)},
547
491
  {"nativePause", "(J)V", reinterpret_cast<void *>(nativePause)},
548
492
  {"nativeStop", "(J)V", reinterpret_cast<void *>(nativeStop)},
549
493
  {"nativeFlush", "(J)Z", reinterpret_cast<void *>(nativeFlush)},
550
494
  {"nativeSetVolume", "(JF)Z", reinterpret_cast<void *>(nativeSetVolume)},
551
495
  {"nativeSetProgressMs", "(JII)Z", reinterpret_cast<void *>(nativeSetProgressMs)},
552
- {"nativeGetProgressMs", "(J)J", reinterpret_cast<void *>(nativeGetProgressMs)},
553
496
  {"nativeIsConnected", "(J)Z", reinterpret_cast<void *>(nativeIsConnected)},
554
- {"nativeIsPlaying", "(J)Z", reinterpret_cast<void *>(nativeIsPlaying)},
555
- {"nativeGetState", "(J)I", reinterpret_cast<void *>(nativeGetState)},
556
- {"nativeGetLatency", "(J)I", reinterpret_cast<void *>(nativeGetLatency)},
557
- {"nativeGetSampleRate", "(J)I", reinterpret_cast<void *>(nativeGetSampleRate)},
558
497
  {"nativeKeepalive", "(J)Z", reinterpret_cast<void *>(nativeKeepalive)},
559
498
  {"nativeGetLastRtspStatus", "(J)I", reinterpret_cast<void *>(nativeGetLastRtspStatus)},
560
499
  {"nativeSetPassword", "(JLjava/lang/String;)V", reinterpret_cast<void *>(nativeSetPassword)},
@@ -576,13 +515,22 @@ jint JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
576
515
 
577
516
  jclass clazz = env->FindClass(sClassName);
578
517
  if (clazz == nullptr) {
579
- __android_log_print(ANDROID_LOG_WARN, TAG,
580
- "AirplayJNI class not loaded yet RegisterNatives deferred");
581
- return JNI_VERSION_1_6;
518
+ /* Leaves a pending NoClassDefFoundError. Clear it and fail the load
519
+ * outright: nothing retries registration, so reporting success here
520
+ * would leave the Kotlin object reporting AirPlay 1 as available with
521
+ * no natives bound, and the first call would throw
522
+ * UnsatisfiedLinkError. Failing makes System.loadLibrary throw, and
523
+ * AirplayJNI's initialiser catches that and sets isAvailable false. */
524
+ env->ExceptionClear();
525
+ __android_log_print(ANDROID_LOG_ERROR, TAG,
526
+ "AirplayJNI class not found — AirPlay 1 unavailable");
527
+ return JNI_ERR;
582
528
  }
583
529
 
584
530
  if (env->RegisterNatives(clazz, sMethods,
585
531
  sizeof(sMethods) / sizeof(sMethods[0])) < 0) {
532
+ /* An unresolved table entry leaves a pending NoSuchMethodError. */
533
+ env->ExceptionClear();
586
534
  __android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed");
587
535
  return JNI_ERR;
588
536
  }
@@ -66,10 +66,11 @@ internal class AirPlayEngine(
66
66
 
67
67
  private val playerListener = object : Player.Listener {
68
68
  override fun onPlaybackStateChanged(playbackState: Int) {
69
- delegate?.onStateMaybeChanged(this@AirPlayEngine, playWhenReadyReason = null)
69
+ // End signal before the generic state callback, as in GaplessEngine.
70
70
  if (playbackState == Player.STATE_ENDED) {
71
71
  delegate?.onPlaybackEnded(this@AirPlayEngine)
72
72
  }
73
+ delegate?.onStateMaybeChanged(this@AirPlayEngine, playWhenReadyReason = null)
73
74
  }
74
75
 
75
76
  override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
@@ -129,15 +130,6 @@ internal class AirPlayEngine(
129
130
 
130
131
  // --- PlaybackEngine — lifecycle ---
131
132
 
132
- override suspend fun prepare(
133
- mediaItems: List<MediaItem>,
134
- startIndex: Int,
135
- startPositionMs: Long,
136
- ) {
137
- exoPlayer.setMediaItems(mediaItems, startIndex, startPositionMs)
138
- exoPlayer.prepare()
139
- }
140
-
141
133
  override fun release() {
142
134
  if (released) return
143
135
  released = true
@@ -168,6 +160,11 @@ internal class AirPlayEngine(
168
160
  startIndex: Int,
169
161
  startPositionMs: Long,
170
162
  ) {
163
+ if (items.isEmpty()) {
164
+ // Draining is not pausing: `playWhenReady` survives an empty playlist,
165
+ // so the next queue seated on this player would start on contact.
166
+ exoPlayer.pause()
167
+ }
171
168
  exoPlayer.setMediaItems(items, startIndex, startPositionMs)
172
169
  // ExoPlayer is IDLE after construction; prepare() is required before any
173
170
  // subsequent play() can take effect (mirrors GaplessEngine).
@@ -272,12 +269,6 @@ internal class AirPlayEngine(
272
269
 
273
270
  // ReplayGain absent on the AirPlay engine — interface defaults
274
271
  // (currentReplayGainData() = null, currentReplayGainActive() = false) apply.
275
-
276
- // --- PlaybackEngine — handoff ---
277
-
278
- override suspend fun handoff(to: PlaybackEngine, atTrackBoundary: Boolean) {
279
- throw NotImplementedError("PlaybackEngine.handoff is not supported by AirPlayEngine")
280
- }
281
272
  }
282
273
 
283
274
  /**
@@ -3,7 +3,7 @@ package com.margelo.nitro.queueplayer
3
3
  import java.net.URI
4
4
 
5
5
  /**
6
- * MIME-type plumbing for the lookahead cache. Mirrors iOS's
6
+ * MIME-type handling for the lookahead cache. Mirrors iOS's
7
7
  * `LookaheadCache.extensionFromMimeType` / `extensionFromUrl` —
8
8
  * lets the Android cache record the same Content-Type signal so
9
9
  * Media3 can be told the format up front via
@@ -526,12 +526,8 @@ class CastManager : HybridCastManagerSpec() {
526
526
  }
527
527
  airplayScope.launch {
528
528
  try {
529
- val session = backend.submitPairingPin(pin)
530
- val pendingId = activeAirplaySession.get()?.let { /* keep existing */ null }
531
- val receiverId = pendingId ?: lastConnectedAirplayReceiverId() ?: ""
532
- if (receiverId.isNotEmpty()) {
533
- installAirplaySession(session, receiverId)
534
- }
529
+ val paired = backend.submitPairingPin(pin)
530
+ installAirplaySession(paired.session, paired.receiverId)
535
531
  promise.resolve(Unit)
536
532
  } catch (t: Throwable) {
537
533
  promise.reject(t)
@@ -540,14 +536,6 @@ class CastManager : HybridCastManagerSpec() {
540
536
  return promise
541
537
  }
542
538
 
543
- /// Look up the most recently visible AirPlay receiver that pair-setup
544
- /// would have stashed against. The pending pair's receiverId isn't
545
- /// directly accessible from the public backend surface; lookup via
546
- /// the discovered snapshot is good enough — the user pairs the device
547
- /// they just saw in the picker.
548
- private fun lastConnectedAirplayReceiverId(): String? =
549
- airplayReceivers.firstOrNull()?.id
550
-
551
539
  override fun getLocalNetworkPermissionState(): CastLocalNetworkPermission =
552
540
  localNetworkPermissionState()
553
541
 
@@ -0,0 +1,77 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import android.content.Context
4
+ import android.net.ConnectivityManager
5
+ import android.net.Network
6
+ import android.net.NetworkCapabilities
7
+
8
+ /**
9
+ * Reports whether the default network is cellular, and fires when that
10
+ * changes.
11
+ *
12
+ * Prefetch downloads whole upcoming tracks, so a consumer that has turned
13
+ * `allowsCellularAccess` off needs the window to empty the moment the device
14
+ * hands off to cellular — otherwise a download started on Wi-Fi keeps pulling
15
+ * over the metered link until some unrelated event reschedules. Reading the
16
+ * transport per reschedule instead would put a synchronous binder call on the
17
+ * app looper on every skip and track advance.
18
+ *
19
+ * A VPN carries its underlying network's transports alongside `TRANSPORT_VPN`,
20
+ * so a cellular-backed VPN still reads as cellular here.
21
+ *
22
+ * Unknown connectivity reads as not-cellular, so a missing service or a
23
+ * revoked callback never silently stops prefetch.
24
+ */
25
+ internal class CellularTransportMonitor(private val context: Context) {
26
+
27
+ /**
28
+ * Fired when [isOnCellular] flips, on the ConnectivityManager callback
29
+ * thread — a listener that touches main-confined state has to hop.
30
+ */
31
+ var onTransportChanged: (() -> Unit)? = null
32
+
33
+ @Volatile
34
+ var isOnCellular: Boolean = false
35
+ private set
36
+
37
+ private var registered = false
38
+
39
+ private val callback = object : ConnectivityManager.NetworkCallback() {
40
+ override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
41
+ update(caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
42
+ }
43
+
44
+ override fun onLost(network: Network) {
45
+ update(false)
46
+ }
47
+ }
48
+
49
+ fun start() {
50
+ if (registered) return
51
+ val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
52
+ ?: return
53
+ // The registration can throw (`TooManyRequestsException` once a process
54
+ // holds 100 callbacks); prefetch then runs without cellular awareness
55
+ // rather than `configure()` failing.
56
+ if (runCatching { cm.registerDefaultNetworkCallback(callback) }.isFailure) return
57
+ registered = true
58
+ // Seed from the active network: the first reschedule can land before the
59
+ // first callback does.
60
+ val caps = cm.activeNetwork?.let { cm.getNetworkCapabilities(it) }
61
+ isOnCellular = caps?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
62
+ }
63
+
64
+ fun stop() {
65
+ if (!registered) return
66
+ val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
67
+ registered = false
68
+ isOnCellular = false
69
+ cm?.let { runCatching { it.unregisterNetworkCallback(callback) } }
70
+ }
71
+
72
+ private fun update(onCellular: Boolean) {
73
+ if (onCellular == isOnCellular) return
74
+ isOnCellular = onCellular
75
+ onTransportChanged?.invoke()
76
+ }
77
+ }