react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.20

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 (660) hide show
  1. package/README.md +35 -21
  2. package/android/build.gradle +35 -12
  3. package/android/gradle.properties +2 -2
  4. package/android/src/main/AndroidManifest.xml +3 -1
  5. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
  6. package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
  7. package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
  8. package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
  9. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
  10. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
  11. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
  12. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
  13. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
  14. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  15. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -7
  16. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  17. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  18. package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
  19. package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
  20. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
  21. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
  22. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
  23. package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
  24. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
  25. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
  26. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
  27. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
  28. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
  29. package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
  30. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  31. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
  32. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
  33. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  34. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
  35. package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
  36. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
  37. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
  38. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
  39. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
  40. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -40
  41. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  42. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
  43. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
  44. package/android/src/main/res/values/strings.xml +25 -5
  45. package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
  46. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
  47. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  48. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
  49. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
  50. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  51. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
  52. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
  53. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
  54. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
  55. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
  56. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  57. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  58. package/android/src/test/java/com/audiobrowser/model/IcyTitleTest.kt +49 -0
  59. package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
  60. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
  61. package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
  62. package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
  63. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  64. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
  65. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  66. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
  67. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  68. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
  69. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
  70. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
  71. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
  72. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  73. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -0
  74. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  75. package/ios/Browser/BrowserConfig.swift +102 -5
  76. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  77. package/ios/Browser/BrowserManager.swift +230 -205
  78. package/ios/Browser/BrowserPathHelper.swift +44 -44
  79. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  80. package/ios/Browser/JsonModels.swift +264 -106
  81. package/ios/Browser/SearchDrillIn.swift +7 -7
  82. package/ios/Browser/SectionPresentation.swift +64 -0
  83. package/ios/Browser/SectionScope.swift +58 -24
  84. package/ios/Browser/StyleResolver.swift +58 -0
  85. package/ios/Browser/TabBarEntries.swift +9 -3
  86. package/ios/Browser/TrackIdTemplate.swift +37 -0
  87. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  88. package/ios/CarPlay/CarPlayController.swift +227 -85
  89. package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
  90. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  91. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  92. package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
  93. package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
  94. package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
  95. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  96. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  97. package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
  98. package/ios/Extension/Section+Copying.swift +29 -0
  99. package/ios/Extension/String+IcyCharset.swift +21 -0
  100. package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
  101. package/ios/Extension/Track+Copying.swift +8 -12
  102. package/ios/Extension/Track+Identity.swift +18 -0
  103. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  104. package/ios/Http/HttpClient.swift +8 -2
  105. package/ios/HybridAudioBrowser.swift +253 -100
  106. package/ios/Model/NitroTypeStubs.swift +125 -23
  107. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  108. package/ios/Model/RemoteCommand.swift +18 -0
  109. package/ios/Model/TrackPlayerError.swift +2 -2
  110. package/ios/Option/SessionCategories.swift +3 -2
  111. package/ios/Player/MediaIntentCriteria.swift +17 -9
  112. package/ios/Player/MediaLoader.swift +33 -9
  113. package/ios/Player/NowPlayingUpdater.swift +54 -6
  114. package/ios/Player/PlaybackCoordinator.swift +13 -6
  115. package/ios/Player/PlaybackErrorHandler.swift +4 -4
  116. package/ios/Player/PlaybackTimer.swift +8 -2
  117. package/ios/Player/QueueManager.swift +46 -13
  118. package/ios/Player/RetryManager.swift +80 -18
  119. package/ios/Player/SeekCompletionHandler.swift +3 -1
  120. package/ios/Player/ShuffleOrder.swift +33 -0
  121. package/ios/Player/SleepTimerHandling.swift +13 -0
  122. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
  123. package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
  124. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  125. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  126. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  127. package/ios/Tests/JsonModelsTests.swift +150 -1
  128. package/ios/Tests/LRUCacheTests.swift +111 -0
  129. package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
  130. package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
  131. package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
  132. package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
  133. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  134. package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
  135. package/ios/Tests/QueueManagerTests.swift +122 -10
  136. package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
  137. package/ios/Tests/RemoteCommandTests.swift +67 -0
  138. package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
  139. package/ios/Tests/RetryManagerTests.swift +89 -0
  140. package/ios/Tests/SearchDrillInTests.swift +16 -16
  141. package/ios/Tests/SectionPresentationTests.swift +90 -0
  142. package/ios/Tests/SectionScopeTests.swift +87 -55
  143. package/ios/Tests/StyleResolverTests.swift +127 -0
  144. package/ios/Tests/Support/TestTypes.swift +8 -1
  145. package/ios/Tests/TabBarEntriesTests.swift +19 -19
  146. package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
  147. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  148. package/ios/Tests/TrackIdentityTests.swift +20 -0
  149. package/ios/Tests/TrackSelectorTests.swift +168 -33
  150. package/ios/TrackPlayer.swift +63 -35
  151. package/ios/TrackPlayerCallbacks.swift +5 -38
  152. package/ios/TrackSelector.swift +45 -30
  153. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  154. package/ios/Util/LRUCache.swift +4 -1
  155. package/ios/Util/NetworkMonitor.swift +43 -26
  156. package/ios/Util/SFSymbolRenderer.swift +24 -2
  157. package/ios/Util/SVGProcessor.swift +9 -2
  158. package/lib/commonjs/AudioBrowser.js +8 -8
  159. package/lib/commonjs/AudioBrowser.js.map +1 -1
  160. package/lib/commonjs/CLAUDE.md +52 -0
  161. package/lib/commonjs/features/browser-config.js +12 -8
  162. package/lib/commonjs/features/browser-config.js.map +1 -1
  163. package/lib/commonjs/features/browser.js +11 -3
  164. package/lib/commonjs/features/browser.js.map +1 -1
  165. package/lib/commonjs/features/carConnection.js +1 -1
  166. package/lib/commonjs/features/equalizer.js +1 -1
  167. package/lib/commonjs/features/errors.js +3 -3
  168. package/lib/commonjs/features/favorites.js +14 -11
  169. package/lib/commonjs/features/favorites.js.map +1 -1
  170. package/lib/commonjs/features/gate.js +16 -1
  171. package/lib/commonjs/features/gate.js.map +1 -1
  172. package/lib/commonjs/features/metadata.js +20 -3
  173. package/lib/commonjs/features/metadata.js.map +1 -1
  174. package/lib/commonjs/features/network.js +1 -1
  175. package/lib/commonjs/features/nowPlaying.js +1 -1
  176. package/lib/commonjs/features/output.js +1 -1
  177. package/lib/commonjs/features/playback/controls.js +4 -2
  178. package/lib/commonjs/features/playback/controls.js.map +1 -1
  179. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  180. package/lib/commonjs/features/playback/playing.js +1 -1
  181. package/lib/commonjs/features/playback/progress.js +3 -2
  182. package/lib/commonjs/features/playback/progress.js.map +1 -1
  183. package/lib/commonjs/features/playback/rate.js +8 -1
  184. package/lib/commonjs/features/playback/rate.js.map +1 -1
  185. package/lib/commonjs/features/playback/state.js +3 -4
  186. package/lib/commonjs/features/playback/state.js.map +1 -1
  187. package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
  188. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
  189. package/lib/commonjs/features/playback/volume.js +1 -1
  190. package/lib/commonjs/features/player/options.js +48 -34
  191. package/lib/commonjs/features/player/options.js.map +1 -1
  192. package/lib/commonjs/features/player/setup.js +18 -9
  193. package/lib/commonjs/features/player/setup.js.map +1 -1
  194. package/lib/commonjs/features/player/validateOptions.js +23 -0
  195. package/lib/commonjs/features/player/validateOptions.js.map +1 -0
  196. package/lib/commonjs/features/queue/activeTrack.js +15 -4
  197. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  198. package/lib/commonjs/features/queue/queue.js +13 -6
  199. package/lib/commonjs/features/queue/queue.js.map +1 -1
  200. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  201. package/lib/commonjs/features/queue/shuffle.js +1 -1
  202. package/lib/commonjs/features/remoteControls.js +13 -92
  203. package/lib/commonjs/features/remoteControls.js.map +1 -1
  204. package/lib/commonjs/features/sleepTimer.js +13 -1
  205. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  206. package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
  207. package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
  208. package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
  209. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  210. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  211. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  212. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  213. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  214. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  215. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  216. package/lib/commonjs/web/NativeAudioBrowser.js +200 -75
  217. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  218. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
  219. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
  220. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  221. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  222. package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
  223. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  224. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  225. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  226. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
  227. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
  228. package/lib/commonjs/web/browser/BrowserManager.js +175 -100
  229. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  230. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  231. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  232. package/lib/commonjs/web/browser/SearchManager.js +18 -23
  233. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  234. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  235. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  236. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  237. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  238. package/lib/commonjs/web/browser/sections.js +89 -0
  239. package/lib/commonjs/web/browser/sections.js.map +1 -0
  240. package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
  241. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  242. package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
  243. package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
  244. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  245. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  246. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  247. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  248. package/lib/module/AudioBrowser.js +2 -1
  249. package/lib/module/AudioBrowser.js.map +1 -1
  250. package/lib/module/CLAUDE.md +52 -0
  251. package/lib/module/features/browser-config.js +12 -8
  252. package/lib/module/features/browser-config.js.map +1 -1
  253. package/lib/module/features/browser.js +11 -3
  254. package/lib/module/features/browser.js.map +1 -1
  255. package/lib/module/features/carConnection.js +1 -1
  256. package/lib/module/features/equalizer.js +1 -1
  257. package/lib/module/features/errors.js +3 -3
  258. package/lib/module/features/favorites.js +14 -11
  259. package/lib/module/features/favorites.js.map +1 -1
  260. package/lib/module/features/gate.js +16 -1
  261. package/lib/module/features/gate.js.map +1 -1
  262. package/lib/module/features/metadata.js +20 -3
  263. package/lib/module/features/metadata.js.map +1 -1
  264. package/lib/module/features/network.js +1 -1
  265. package/lib/module/features/nowPlaying.js +1 -1
  266. package/lib/module/features/output.js +1 -1
  267. package/lib/module/features/playback/controls.js +4 -2
  268. package/lib/module/features/playback/controls.js.map +1 -1
  269. package/lib/module/features/playback/playWhenReady.js +1 -1
  270. package/lib/module/features/playback/playing.js +1 -1
  271. package/lib/module/features/playback/progress.js +3 -2
  272. package/lib/module/features/playback/progress.js.map +1 -1
  273. package/lib/module/features/playback/rate.js +8 -1
  274. package/lib/module/features/playback/rate.js.map +1 -1
  275. package/lib/module/features/playback/state.js +3 -4
  276. package/lib/module/features/playback/state.js.map +1 -1
  277. package/lib/module/features/playback/trackPlaybackTime.js +0 -5
  278. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
  279. package/lib/module/features/playback/volume.js +1 -1
  280. package/lib/module/features/player/options.js +47 -32
  281. package/lib/module/features/player/options.js.map +1 -1
  282. package/lib/module/features/player/setup.js +17 -8
  283. package/lib/module/features/player/setup.js.map +1 -1
  284. package/lib/module/features/player/validateOptions.js +19 -0
  285. package/lib/module/features/player/validateOptions.js.map +1 -0
  286. package/lib/module/features/queue/activeTrack.js +15 -4
  287. package/lib/module/features/queue/activeTrack.js.map +1 -1
  288. package/lib/module/features/queue/queue.js +13 -6
  289. package/lib/module/features/queue/queue.js.map +1 -1
  290. package/lib/module/features/queue/repeatMode.js +1 -1
  291. package/lib/module/features/queue/shuffle.js +1 -1
  292. package/lib/module/features/remoteControls.js +13 -92
  293. package/lib/module/features/remoteControls.js.map +1 -1
  294. package/lib/module/features/sleepTimer.js +13 -1
  295. package/lib/module/features/sleepTimer.js.map +1 -1
  296. package/lib/module/test-utils/reactNativeStub.js +58 -0
  297. package/lib/module/test-utils/reactNativeStub.js.map +1 -0
  298. package/lib/module/utils/LazyNativeEmitter.js +28 -14
  299. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  300. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  301. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  302. package/lib/module/utils/getTrackIdentity.js +20 -0
  303. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  304. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  305. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  306. package/lib/module/web/NativeAudioBrowser.js +200 -75
  307. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  308. package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
  309. package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
  310. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  311. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  312. package/lib/module/web/TrackPlayer/Player.js +254 -39
  313. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  314. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  315. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  316. package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
  317. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
  318. package/lib/module/web/browser/BrowserManager.js +175 -100
  319. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  320. package/lib/module/web/browser/FavoriteManager.js +31 -21
  321. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  322. package/lib/module/web/browser/SearchManager.js +18 -23
  323. package/lib/module/web/browser/SearchManager.js.map +1 -1
  324. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  325. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  326. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  327. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  328. package/lib/module/web/browser/sections.js +83 -0
  329. package/lib/module/web/browser/sections.js.map +1 -0
  330. package/lib/module/web/http/RequestConfigBuilder.js +110 -27
  331. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  332. package/lib/module/web/http/followMediaRedirect.js +215 -0
  333. package/lib/module/web/http/followMediaRedirect.js.map +1 -0
  334. package/lib/module/web/player/NowPlayingManager.js +4 -3
  335. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  336. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  337. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  338. package/lib/typescript/package.json +1 -0
  339. package/lib/typescript/src/AudioBrowser.d.ts +1 -1
  340. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  341. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  342. package/lib/typescript/src/features/browser.d.ts +10 -2
  343. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  344. package/lib/typescript/src/features/carConnection.d.ts +1 -1
  345. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  346. package/lib/typescript/src/features/errors.d.ts +3 -3
  347. package/lib/typescript/src/features/favorites.d.ts +14 -11
  348. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  349. package/lib/typescript/src/features/gate.d.ts +18 -2
  350. package/lib/typescript/src/features/gate.d.ts.map +1 -1
  351. package/lib/typescript/src/features/metadata.d.ts +20 -3
  352. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  353. package/lib/typescript/src/features/network.d.ts +1 -1
  354. package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
  355. package/lib/typescript/src/features/output.d.ts +1 -1
  356. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  357. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  358. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  359. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  360. package/lib/typescript/src/features/playback/progress.d.ts +1 -1
  361. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  362. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  363. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  364. package/lib/typescript/src/features/playback/state.d.ts +3 -4
  365. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  366. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  367. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  368. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  369. package/lib/typescript/src/features/player/options.d.ts +108 -56
  370. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  371. package/lib/typescript/src/features/player/setup.d.ts +14 -4
  372. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  373. package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
  374. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
  375. package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
  376. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  377. package/lib/typescript/src/features/queue/queue.d.ts +7 -4
  378. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  379. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  380. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  381. package/lib/typescript/src/features/remoteControls.d.ts +23 -22
  382. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  383. package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
  384. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  385. package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
  386. package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
  387. package/lib/typescript/src/types/browser-native.d.ts +3 -2
  388. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  389. package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
  390. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  391. package/lib/typescript/src/types/browser.d.ts +82 -57
  392. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  393. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
  394. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  395. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  396. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  397. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  398. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  399. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  400. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  401. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
  402. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
  403. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
  404. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  405. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  406. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  407. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  408. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  409. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  410. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  411. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  412. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  413. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  414. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  415. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  416. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  417. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
  418. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
  419. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  420. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
  421. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  422. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  423. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  424. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  425. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
  426. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
  427. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  428. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  429. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  430. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
  431. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  432. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
  433. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
  434. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
  435. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  436. package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
  437. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  438. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  439. package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
  440. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
  441. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
  442. package/nitrogen/generated/android/c++/JSection.hpp +116 -0
  443. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  444. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  445. package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
  446. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  447. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  448. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
  449. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
  450. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  451. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
  452. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  453. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  454. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  455. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
  456. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  457. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
  458. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  459. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  460. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
  461. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
  462. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  463. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  464. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
  465. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
  466. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
  467. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
  468. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  469. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  470. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -12
  471. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  472. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
  473. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
  474. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
  475. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
  476. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
  477. package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
  478. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  479. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  480. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  481. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
  482. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  483. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
  484. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
  485. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  486. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  487. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
  488. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
  489. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  490. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  491. package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
  492. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
  493. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
  494. package/nitrogen/generated/ios/swift/Section.swift +82 -0
  495. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  496. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  497. package/nitrogen/generated/ios/swift/Track.swift +28 -63
  498. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  499. package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
  500. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
  501. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  502. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  503. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  504. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  505. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
  506. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
  507. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  508. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  509. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
  510. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
  511. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  512. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  513. package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
  514. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
  515. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
  516. package/nitrogen/generated/shared/c++/Section.hpp +102 -0
  517. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  518. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  519. package/nitrogen/generated/shared/c++/Track.hpp +18 -30
  520. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  521. package/package.json +24 -11
  522. package/src/AudioBrowser.ts +2 -1
  523. package/src/CLAUDE.md +52 -0
  524. package/src/features/battery.test.ts +79 -0
  525. package/src/features/browser-config.test.ts +21 -6
  526. package/src/features/browser-config.ts +12 -8
  527. package/src/features/browser.test.ts +1 -1
  528. package/src/features/browser.ts +11 -3
  529. package/src/features/carConnection.ts +1 -1
  530. package/src/features/equalizer.ts +1 -1
  531. package/src/features/errors.ts +3 -3
  532. package/src/features/favorites.ts +14 -11
  533. package/src/features/gate.ts +16 -1
  534. package/src/features/hook-wiring.test.ts +302 -0
  535. package/src/features/metadata.ts +20 -3
  536. package/src/features/network.ts +1 -1
  537. package/src/features/nowPlaying.ts +1 -1
  538. package/src/features/output.ts +1 -1
  539. package/src/features/playback/controls.ts +4 -2
  540. package/src/features/playback/playWhenReady.ts +1 -1
  541. package/src/features/playback/playing.ts +1 -1
  542. package/src/features/playback/progress.test.ts +169 -0
  543. package/src/features/playback/progress.ts +3 -2
  544. package/src/features/playback/rate.ts +8 -1
  545. package/src/features/playback/state.ts +3 -4
  546. package/src/features/playback/trackPlaybackTime.test.ts +16 -6
  547. package/src/features/playback/trackPlaybackTime.ts +1 -6
  548. package/src/features/playback/volume.ts +1 -1
  549. package/src/features/player/options.ts +126 -76
  550. package/src/features/player/setup.test.ts +24 -7
  551. package/src/features/player/setup.ts +22 -10
  552. package/src/features/player/validateOptions.ts +21 -0
  553. package/src/features/queue/activeTrack.ts +15 -4
  554. package/src/features/queue/queue.ts +13 -6
  555. package/src/features/queue/repeatMode.ts +1 -1
  556. package/src/features/queue/shuffle.ts +1 -1
  557. package/src/features/remoteControls.ts +13 -92
  558. package/src/features/sleepTimer.test.ts +172 -0
  559. package/src/features/sleepTimer.ts +9 -1
  560. package/src/nitro-stub-drift.test.ts +277 -0
  561. package/src/public-surface.test.ts +147 -0
  562. package/src/specs/audio-browser.nitro.ts +5 -4
  563. package/src/style-resolution.test.ts +210 -0
  564. package/src/test-utils/reactNativeStub.ts +60 -0
  565. package/src/types/browser-native.ts +7 -3
  566. package/src/types/browser-nodes.ts +396 -134
  567. package/src/types/browser.ts +85 -59
  568. package/src/utils/LazyNativeEmitter.ts +28 -15
  569. package/src/utils/NativeUpdatedValue.ts +29 -13
  570. package/src/utils/getTrackIdentity.ts +20 -0
  571. package/src/utils/useDebug.test.ts +182 -0
  572. package/src/utils/useNativeUpdatedValue.test.ts +103 -0
  573. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  574. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
  575. package/src/web/NativeAudioBrowser.ts +239 -82
  576. package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
  577. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
  578. package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
  579. package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
  580. package/src/web/TrackPlayer/Player.load.test.ts +137 -1
  581. package/src/web/TrackPlayer/Player.ts +284 -37
  582. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  583. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  584. package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
  585. package/src/web/__tests__/lifecycle.test.ts +345 -0
  586. package/src/web/__tests__/lifecycleHarness.ts +351 -0
  587. package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
  588. package/src/web/__tests__/loadSupersession.test.ts +175 -0
  589. package/src/web/__tests__/retry.test.ts +169 -0
  590. package/src/web/browser/BrowserManager.ts +209 -111
  591. package/src/web/browser/FavoriteManager.test.ts +84 -0
  592. package/src/web/browser/FavoriteManager.ts +28 -23
  593. package/src/web/browser/SearchManager.test.ts +75 -0
  594. package/src/web/browser/SearchManager.ts +25 -28
  595. package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
  596. package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
  597. package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
  598. package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
  599. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  600. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  601. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  602. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  603. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  604. package/src/web/browser/sections.test.ts +91 -0
  605. package/src/web/browser/sections.ts +72 -0
  606. package/src/web/http/RequestConfigBuilder.test.ts +241 -1
  607. package/src/web/http/RequestConfigBuilder.ts +140 -33
  608. package/src/web/http/followMediaRedirect.test.ts +289 -0
  609. package/src/web/http/followMediaRedirect.ts +253 -0
  610. package/src/web/player/NowPlayingManager.ts +4 -3
  611. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  612. package/src/web/util/BrowserPathHelper.ts +61 -13
  613. package/website/guide/android-auto.md +114 -0
  614. package/website/guide/android-certificates.md +409 -0
  615. package/website/guide/artwork.md +255 -0
  616. package/website/guide/audio-output.md +135 -0
  617. package/website/guide/automotive.md +86 -0
  618. package/website/guide/basic-usage.md +151 -0
  619. package/website/guide/battery.md +115 -0
  620. package/website/guide/browser.md +581 -0
  621. package/website/guide/carplay.md +620 -0
  622. package/website/guide/configuration.md +173 -0
  623. package/website/guide/equalizer.md +231 -0
  624. package/website/guide/errors.md +310 -0
  625. package/website/guide/favorites.md +154 -0
  626. package/website/guide/gate.md +143 -0
  627. package/website/guide/getting-started.md +46 -0
  628. package/website/guide/hooks.md +238 -0
  629. package/website/guide/live-streams.md +125 -0
  630. package/website/guide/metadata.md +239 -0
  631. package/website/guide/migrating-from-track-player.md +244 -0
  632. package/website/guide/native-callback-fetch.md +127 -0
  633. package/website/guide/network.md +86 -0
  634. package/website/guide/now-playing.md +175 -0
  635. package/website/guide/playback.md +328 -0
  636. package/website/guide/queue.md +300 -0
  637. package/website/guide/remote-controls.md +256 -0
  638. package/website/guide/search.md +255 -0
  639. package/website/guide/sleep-timer.md +268 -0
  640. package/website/guide/track.md +247 -0
  641. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  642. package/ios/Model/MediaURL.swift +0 -67
  643. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  644. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  645. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
  646. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  647. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  648. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
  649. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  653. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  654. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  655. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  656. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
  657. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  658. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  659. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
  660. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
@@ -1,19 +1,35 @@
1
1
  package com.audiobrowser.browser
2
2
 
3
+ import androidx.media3.common.C
4
+ import androidx.media3.common.MediaItem
5
+ import com.audiobrowser.TestFixtures.resolvedTrack
6
+ import com.audiobrowser.TestFixtures.section
7
+ import com.audiobrowser.TestFixtures.sectionStyle
8
+ import com.audiobrowser.TestFixtures.staticRoute
9
+ import com.audiobrowser.TestFixtures.track
10
+ import com.audiobrowser.util.BrowserPathHelper
3
11
  import kotlinx.coroutines.runBlocking
4
- import org.junit.Assert.*
12
+ import org.junit.Assert.assertEquals
13
+ import org.junit.Assert.assertNull
14
+ import org.junit.Assert.fail
5
15
  import org.junit.Before
6
16
  import org.junit.Test
7
17
  import org.junit.runner.RunWith
8
18
  import org.robolectric.RobolectricTestRunner
9
19
 
10
- // TODO: Most tests disabled — BrowserConfig API changed (routes is now Array<NativeRouteEntry>).
11
- // When revived, add expandQueueFromContextualUrl glue tests: found id → its
12
- // SectionScope section as the queue; vanished id → null (single-track
13
- // fallback). The section logic and the caller fallback are covered
14
- // (SectionScopeTest, TrackSelectorTests); the wiring between them is not.
15
- // Tests that used BrowserSource/BrowserList need to be rewritten.
16
-
20
+ /**
21
+ * Navigation and queue-expansion glue.
22
+ *
23
+ * [BrowserManager.expandQueueFromContextualPath] is the seam between the page a listener tapped in
24
+ * and the queue that starts playing. Its two halves are covered on their own — the section logic in
25
+ * [SectionScopeTest], the caller's single-track fallback in the player tests — but the wiring
26
+ * between them is what decides whether tapping row 3 of a page resumes the right station, and only
27
+ * these tests exercise it.
28
+ *
29
+ * Routes serve `browseStatic`, the one browse arm that needs neither the JNI bridge nor HTTP.
30
+ * Robolectric is required for `android.net.Uri` (contextual-path parsing) and
31
+ * `android.util.LruCache` (the content cache).
32
+ */
17
33
  @RunWith(RobolectricTestRunner::class)
18
34
  class BrowserManagerTest {
19
35
 
@@ -24,6 +40,31 @@ class BrowserManagerTest {
24
40
  browserManager = BrowserManager()
25
41
  }
26
42
 
43
+ /** Configures a single static route at [path] serving [children] as its page. */
44
+ private fun servePage(
45
+ path: String,
46
+ vararg children: com.margelo.nitro.audiobrowser.Track,
47
+ singleTrack: Boolean = false,
48
+ ) {
49
+ browserManager.config =
50
+ BrowserConfig(
51
+ routes =
52
+ arrayOf(staticRoute(path, resolvedTrack(path = path, children = arrayOf(*children)))),
53
+ singleTrack = singleTrack,
54
+ )
55
+ }
56
+
57
+ /** Configures a single static route at [path] serving [sections] as its page. */
58
+ private fun serveSections(path: String, vararg sections: com.margelo.nitro.audiobrowser.Section) {
59
+ browserManager.config =
60
+ BrowserConfig(
61
+ routes =
62
+ arrayOf(staticRoute(path, resolvedTrack(path = path, sections = arrayOf(*sections))))
63
+ )
64
+ }
65
+
66
+ private fun contextual(path: String, trackId: String) = BrowserPathHelper.build(path, trackId)
67
+
27
68
  @Test
28
69
  fun `getPath returns default path initially`() {
29
70
  assertEquals("/", browserManager.getPath())
@@ -64,4 +105,301 @@ class BrowserManagerTest {
64
105
  } catch (_: ContentNotFoundException) {}
65
106
  }
66
107
  }
108
+
109
+ @Test
110
+ fun `navigate serves a static route and gives playable children contextual urls`() = runBlocking {
111
+ servePage("/library", track(src = "a", title = "A"), track(src = "b", title = "B"))
112
+
113
+ val resolved = browserManager.navigate("/library")
114
+
115
+ assertEquals(listOf("A", "B"), resolved.flattenedChildren?.map { it.title })
116
+ // The tap target: a child's path is rewritten to point back at this page
117
+ // plus its own id and page position (the duplicate-identity tie-breaker).
118
+ assertEquals(
119
+ listOf(
120
+ BrowserPathHelper.build("/library", "a", 0),
121
+ BrowserPathHelper.build("/library", "b", 1),
122
+ ),
123
+ resolved.flattenedChildren?.map { it.path },
124
+ )
125
+ assertEquals("/library", browserManager.getPath())
126
+ }
127
+
128
+ // MARK: - expandQueueFromContextualPath
129
+
130
+ @Test
131
+ fun `expandQueueFromContextualPath scopes the queue to the tapped section`() = runBlocking {
132
+ serveSections(
133
+ "/home",
134
+ section(title = "Recent", children = arrayOf(track(src = "a"), track(src = "b"))),
135
+ section(title = "Popular", children = arrayOf(track(src = "c"), track(src = "d"))),
136
+ )
137
+
138
+ val expanded = browserManager.expandQueueFromContextualPath(contextual("/home", "c"))
139
+
140
+ // "Popular" only — a page aggregating sections must not leak next/previous across them.
141
+ val (queue, index) = requireNotNull(expanded)
142
+ assertEquals(listOf("c", "d"), queue.map { it.src })
143
+ assertEquals(0, index)
144
+ }
145
+
146
+ @Test
147
+ fun `expandQueueFromContextualPath indexes the tapped track within its section`() = runBlocking {
148
+ serveSections(
149
+ "/home",
150
+ section(title = "Recent", children = arrayOf(track(src = "a"))),
151
+ section(title = "Popular", children = arrayOf(track(src = "b"), track(src = "c"))),
152
+ )
153
+
154
+ val (queue, index) =
155
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "c")))
156
+
157
+ assertEquals(listOf("b", "c"), queue.map { it.src })
158
+ assertEquals(1, index)
159
+ }
160
+
161
+ @Test
162
+ fun `expandQueueFromContextualPath pins the tapped copy of a duplicate identity`() = runBlocking {
163
+ // The same src twice in one section (normal for music playlists): the
164
+ // stamped page index selects the tapped copy, so "next" continues from
165
+ // there instead of from the first copy.
166
+ servePage("/playlist", track(src = "a"), track(src = "b"), track(src = "a"), track(src = "c"))
167
+
168
+ val (queue, index) =
169
+ requireNotNull(
170
+ browserManager.expandQueueFromContextualPath(BrowserPathHelper.build("/playlist", "a", 2))
171
+ )
172
+
173
+ assertEquals(listOf("a", "b", "a", "c"), queue.map { it.src })
174
+ assertEquals(2, index)
175
+ }
176
+
177
+ @Test
178
+ fun `expandQueueFromContextualPath scopes a cross-section duplicate to the tapped section`() =
179
+ runBlocking {
180
+ // The same identity in a rail section AND the list section below it:
181
+ // the stamped flat index resolves the tap to the list section.
182
+ serveSections(
183
+ "/home",
184
+ section(
185
+ title = "Row",
186
+ style =
187
+ sectionStyle(
188
+ display = com.margelo.nitro.audiobrowser.StyleDisplay.GRID,
189
+ artworkRendering = null,
190
+ gridWrap = false,
191
+ ),
192
+ children = arrayOf(track(src = "dup")),
193
+ ),
194
+ section(title = "Stations", children = arrayOf(track(src = "dup"), track(src = "b"))),
195
+ )
196
+
197
+ val (queue, index) =
198
+ requireNotNull(
199
+ browserManager.expandQueueFromContextualPath(BrowserPathHelper.build("/home", "dup", 1))
200
+ )
201
+
202
+ assertEquals(listOf("dup", "b"), queue.map { it.src })
203
+ assertEquals(0, index)
204
+ }
205
+
206
+ @Test
207
+ fun `expandQueueFromContextualPath falls back to the first match on a stale index`() =
208
+ runBlocking {
209
+ // The list shifted since the stamp: the child at the index no longer
210
+ // carries the identity, so resolution ignores the index and falls back
211
+ // to the first identity match instead of aborting.
212
+ servePage("/home", track(src = "x"), track(src = "a"), track(src = "b"))
213
+
214
+ val (queue, index) =
215
+ requireNotNull(
216
+ browserManager.expandQueueFromContextualPath(BrowserPathHelper.build("/home", "a", 0))
217
+ )
218
+
219
+ assertEquals(listOf("x", "a", "b"), queue.map { it.src })
220
+ assertEquals(1, index)
221
+ }
222
+
223
+ @Test
224
+ fun `expandQueueFromContextualPath returns null when the id has vanished from the page`() =
225
+ runBlocking {
226
+ // The stored path points at a track the page no longer lists — the page changed under a
227
+ // resumed session. Queueing the current list would resume the wrong station, so expansion
228
+ // aborts and the caller falls back to the stored single track.
229
+ servePage("/home", track(src = "a"), track(src = "b"))
230
+
231
+ assertNull(browserManager.expandQueueFromContextualPath(contextual("/home", "gone")))
232
+ }
233
+
234
+ @Test
235
+ fun `expandQueueFromContextualPath returns null for a non-contextual path`() = runBlocking {
236
+ servePage("/home", track(src = "a"))
237
+
238
+ assertNull(browserManager.expandQueueFromContextualPath("/home"))
239
+ }
240
+
241
+ @Test
242
+ fun `expandQueueFromContextualPath returns null when the page has no children`() = runBlocking {
243
+ browserManager.config =
244
+ BrowserConfig(routes = arrayOf(staticRoute("/home", resolvedTrack(path = "/home"))))
245
+
246
+ assertNull(browserManager.expandQueueFromContextualPath(contextual("/home", "a")))
247
+ }
248
+
249
+ @Test
250
+ fun `expandQueueFromContextualPath scopes a rail section to its own tiles`() = runBlocking {
251
+ serveSections(
252
+ "/home",
253
+ section(
254
+ title = "Most Played",
255
+ style =
256
+ sectionStyle(
257
+ display = com.margelo.nitro.audiobrowser.StyleDisplay.GRID,
258
+ artworkRendering = null,
259
+ gridWrap = false,
260
+ ),
261
+ children = arrayOf(track(src = "r1"), track(src = "r2")),
262
+ ),
263
+ section(children = arrayOf(track(src = "a"))),
264
+ )
265
+
266
+ val (queue, index) =
267
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "r2")))
268
+
269
+ // A tile tap queues its section, not the list rows beside it.
270
+ assertEquals(listOf("r1", "r2"), queue.map { it.src })
271
+ assertEquals(1, index)
272
+ }
273
+
274
+ @Test
275
+ fun `expandQueueFromContextualPath drops unplayable siblings`() = runBlocking {
276
+ serveSections(
277
+ "/home",
278
+ section(
279
+ title = "Mixed",
280
+ children =
281
+ arrayOf(
282
+ track(src = "a"),
283
+ // A browsable row sitting inside the same section — no src, so not queueable.
284
+ track(src = null).copy(path = "/sub"),
285
+ track(src = "b"),
286
+ ),
287
+ ),
288
+ )
289
+
290
+ val (queue, index) =
291
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "b")))
292
+
293
+ assertEquals(listOf("a", "b"), queue.map { it.src })
294
+ assertEquals(1, index)
295
+ }
296
+
297
+ @Test
298
+ fun `navigate stamps the identity, not the src, into id-bearing children's contextual urls`() =
299
+ runBlocking {
300
+ servePage(
301
+ "/home",
302
+ track(id = "stable-a", src = "https://s/a.mp3", title = "A"),
303
+ track(src = "https://s/b.mp3", title = "B"),
304
+ )
305
+
306
+ val resolved = browserManager.navigate("/home")
307
+
308
+ // id when non-blank, else src — Track.identity (ADR 0008).
309
+ assertEquals(
310
+ listOf(
311
+ BrowserPathHelper.build("/home", "stable-a", 0),
312
+ BrowserPathHelper.build("/home", "https://s/b.mp3", 1),
313
+ ),
314
+ resolved.flattenedChildren?.map { it.path },
315
+ )
316
+ }
317
+
318
+ @Test
319
+ fun `expandQueueFromContextualPath finds the selected index by identity`() = runBlocking {
320
+ servePage(
321
+ "/home",
322
+ track(id = "stable-a", src = "https://s/a.mp3"),
323
+ track(id = "stable-b", src = "https://s/b.mp3"),
324
+ track(id = "stable-c", src = "https://s/c.mp3"),
325
+ )
326
+
327
+ val (queue, index) =
328
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "stable-c")))
329
+
330
+ assertEquals(listOf("stable-a", "stable-b", "stable-c"), queue.map { it.id })
331
+ assertEquals(2, index)
332
+ }
333
+
334
+ @Test
335
+ fun `getCachedTrack resolves an id-valued trackId through the cache`() = runBlocking {
336
+ servePage("/home", track(id = "stable-b", src = "https://s/b.mp3", title = "B"))
337
+ browserManager.navigate("/home")
338
+
339
+ // A contextual mediaId whose __trackId is the identity (the id), with a parent path that was
340
+ // never cached directly — the extract-then-lookup path must find it via the id key.
341
+ val mediaId = contextual("/elsewhere", "stable-b")
342
+
343
+ assertEquals("B", browserManager.getCachedTrack(mediaId)?.title)
344
+ }
345
+
346
+ @Test
347
+ fun `expandQueueFromContextualPath honours singleTrack`() = runBlocking {
348
+ servePage("/home", track(src = "a"), track(src = "b"), singleTrack = true)
349
+
350
+ val (queue, index) =
351
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "b")))
352
+
353
+ assertEquals(listOf("b"), queue.map { it.src })
354
+ assertEquals(0, index)
355
+ }
356
+
357
+ // --- Track.disabled: queue expansion excludes unavailable tracks ---
358
+
359
+ @Test
360
+ fun `expandQueueFromContextualPath excludes disabled tracks`() = runBlocking {
361
+ servePage(
362
+ "/home",
363
+ track(src = "a"),
364
+ track(src = "unavailable", disabled = true),
365
+ track(src = "b"),
366
+ )
367
+
368
+ val (queue, index) =
369
+ requireNotNull(browserManager.expandQueueFromContextualPath(contextual("/home", "b")))
370
+
371
+ assertEquals(listOf("a", "b"), queue.map { it.src })
372
+ assertEquals(1, index)
373
+ }
374
+
375
+ @Test
376
+ fun `expandQueueFromContextualPath aborts when the tapped track is disabled`() = runBlocking {
377
+ // A disabled track never plays: with the tapped identity excluded from the
378
+ // playable list, expansion aborts, and every caller's fallback refuses the
379
+ // disabled track itself — navigateTrack's guard on tap,
380
+ // onPlaybackResumption's refusal on resume.
381
+ servePage("/home", track(src = "a"), track(src = "unavailable", disabled = true))
382
+
383
+ assertNull(browserManager.expandQueueFromContextualPath(contextual("/home", "unavailable")))
384
+ }
385
+
386
+ @Test
387
+ fun `resolveMediaItemsForPlayback refuses a disabled track on the legacy INDEX_UNSET path`() =
388
+ runBlocking {
389
+ // Android Auto and Assistant request playback through Media3's legacy
390
+ // path, which passes C.INDEX_UNSET ("the default item") — the refusal
391
+ // must cover it, or a stale browse row for a now-disabled track plays.
392
+ servePage("/home", track(src = "unavailable", disabled = true))
393
+ browserManager.resolve("/home")
394
+
395
+ val item = MediaItem.Builder().setMediaId("unavailable").build()
396
+
397
+ try {
398
+ browserManager.resolveMediaItemsForPlayback(listOf(item), C.INDEX_UNSET, 0)
399
+ fail("Expected refusal of a disabled track")
400
+ } catch (expected: IllegalStateException) {
401
+ // Refused by failing the future: Media3 surfaces an error to the
402
+ // controller and live playback stays untouched.
403
+ }
404
+ }
67
405
  }
@@ -91,6 +91,44 @@ class BrowserUrlResolutionTest {
91
91
  assertEquals("https://api.example.com/artwork/abc", source?.uri)
92
92
  }
93
93
 
94
+ @Test
95
+ fun `resolveArtworkUrl leaves the id token literal for a track without an id`() = runTest {
96
+ bm.config =
97
+ BrowserConfig(
98
+ request = transformableConfig(baseUrl = "https://api.example.com"),
99
+ artwork = artworkConfig(path = "/artwork/{id}"),
100
+ )
101
+ // The config still runs (no blanket skip); the token stays literal so the resulting request
102
+ // 404s with a self-describing URL.
103
+ val source = bm.resolveArtworkUrl(track(artwork = null, id = null))
104
+ assertEquals("https://api.example.com/artwork/{id}", source?.uri)
105
+ }
106
+
107
+ @Test
108
+ fun `resolveArtworkUrl treats a blank id like a missing one`() = runTest {
109
+ bm.config =
110
+ BrowserConfig(
111
+ request = transformableConfig(baseUrl = "https://api.example.com"),
112
+ artwork = artworkConfig(path = "/artwork/{id}"),
113
+ )
114
+ val source = bm.resolveArtworkUrl(track(artwork = null, id = ""))
115
+ assertEquals("https://api.example.com/artwork/{id}", source?.uri)
116
+ }
117
+
118
+ @Test
119
+ fun `resolveArtworkUrl leaves the id token literal in header values without an id`() = runTest {
120
+ bm.config =
121
+ BrowserConfig(
122
+ request = transformableConfig(baseUrl = "https://api.example.com"),
123
+ artwork = artworkConfig(path = "/artwork", headers = mapOf("x-track" to "{id}")),
124
+ )
125
+ val source = bm.resolveArtworkUrl(track(artwork = null, id = null))
126
+ assertEquals("{id}", source?.headers?.get("x-track"))
127
+
128
+ val substituted = bm.resolveArtworkUrl(track(artwork = null, id = "abc"))
129
+ assertEquals("abc", substituted?.headers?.get("x-track"))
130
+ }
131
+
94
132
  @Test
95
133
  fun `resolveArtworkUrl applies image query params from the image context`() = runTest {
96
134
  bm.config =
@@ -0,0 +1,277 @@
1
+ package com.audiobrowser.browser
2
+
3
+ import com.audiobrowser.TestFixtures.browseTrack
4
+ import com.audiobrowser.TestFixtures.section
5
+ import com.audiobrowser.TestFixtures.sectionStyle
6
+ import com.audiobrowser.TestFixtures.track
7
+ import com.audiobrowser.TestFixtures.trackStyle
8
+ import com.margelo.nitro.audiobrowser.ArtworkRendering
9
+ import com.margelo.nitro.audiobrowser.CardImage
10
+ import com.margelo.nitro.audiobrowser.GridTile
11
+ import com.margelo.nitro.audiobrowser.ImageShape
12
+ import com.margelo.nitro.audiobrowser.Section
13
+ import com.margelo.nitro.audiobrowser.SectionStyle
14
+ import com.margelo.nitro.audiobrowser.StyleDisplay
15
+ import org.junit.Assert.assertEquals
16
+ import org.junit.Assert.assertTrue
17
+ import org.junit.Test
18
+
19
+ /**
20
+ * The dev diagnostic for declarations that can never render (ADR 0011): the recovery for the
21
+ * compile-time invalid-combination errors the block model deliberately gave up. Structural
22
+ * inertness only — a surface that can't draw a property it understands is intended usage, never a
23
+ * finding. Mirrors `ios/Tests/InertStyleDiagnosticTests.swift`.
24
+ */
25
+ class InertStyleDiagnosticTest {
26
+
27
+ private fun findings(
28
+ sections: List<Section>,
29
+ page: SectionStyle? = null,
30
+ path: String = "/home",
31
+ ) = InertStyleDiagnostic.findings(path, page, sections)
32
+
33
+ // Container properties
34
+
35
+ @Test
36
+ fun `gridWrap on a list section is inert`() {
37
+ val found = findings(listOf(section(style = sectionStyle(gridWrap = false), title = "Recent")))
38
+ assertEquals(1, found.size)
39
+ assertTrue(found[0], found[0].contains("section 'Recent'"))
40
+ assertTrue(found[0], found[0].contains("style.gridWrap"))
41
+ assertTrue(found[0], found[0].contains("display 'list'"))
42
+ assertTrue(found[0], found[0].contains("/home"))
43
+ }
44
+
45
+ @Test
46
+ fun `gridWrap in a grid is live`() {
47
+ val style = sectionStyle(gridWrap = false, display = StyleDisplay.GRID)
48
+ assertTrue(findings(listOf(section(style = style))).isEmpty())
49
+ }
50
+
51
+ @Test
52
+ fun `a section inherits the page's grid display`() {
53
+ // `display` resolves by scope override, so the page's grid makes the section's `gridWrap` live
54
+ // without the section restating it.
55
+ val found =
56
+ findings(
57
+ listOf(section(style = sectionStyle(gridWrap = false))),
58
+ page = sectionStyle(display = StyleDisplay.GRID),
59
+ )
60
+ assertTrue(found.isEmpty())
61
+ }
62
+
63
+ @Test
64
+ fun `gridTile on a list section is inert`() {
65
+ val found = findings(listOf(section(style = sectionStyle(gridTile = GridTile.CARD))))
66
+ assertEquals(1, found.size)
67
+ assertTrue(found[0], found[0].contains("style.gridTile"))
68
+ assertTrue(found[0], found[0].contains("section #1"))
69
+ }
70
+
71
+ // Item properties
72
+
73
+ @Test
74
+ fun `card properties need the card treatment`() {
75
+ val plainGrid =
76
+ sectionStyle(
77
+ display = StyleDisplay.GRID,
78
+ cardTint = "#1e3a8a",
79
+ cardImage = CardImage.BACKGROUND,
80
+ )
81
+ val found = findings(listOf(section(style = plainGrid, title = "Featured")))
82
+ assertEquals(2, found.size)
83
+ assertTrue(found.any { it.contains("style.cardTint") })
84
+ assertTrue(found.any { it.contains("style.cardImage") })
85
+ assertTrue(found.all { it.contains("display 'grid', gridTile 'plain'") })
86
+ }
87
+
88
+ @Test
89
+ fun `card properties in a card grid are live`() {
90
+ val cards =
91
+ sectionStyle(
92
+ display = StyleDisplay.GRID,
93
+ gridTile = GridTile.CARD,
94
+ cardTint = "#1e3a8a",
95
+ cardImage = CardImage.BACKGROUND,
96
+ )
97
+ assertTrue(findings(listOf(section(style = cards))).isEmpty())
98
+ }
99
+
100
+ @Test
101
+ fun `imageShape needs a shaped tile`() {
102
+ val list = sectionStyle(imageShape = ImageShape.CIRCULAR)
103
+ val cards =
104
+ sectionStyle(
105
+ display = StyleDisplay.GRID,
106
+ gridTile = GridTile.CARD,
107
+ imageShape = ImageShape.CIRCULAR,
108
+ )
109
+ val condensed =
110
+ sectionStyle(
111
+ display = StyleDisplay.GRID,
112
+ gridTile = GridTile.CONDENSED,
113
+ imageShape = ImageShape.CIRCULAR,
114
+ )
115
+ assertEquals(1, findings(listOf(section(style = list))).size)
116
+ assertEquals(1, findings(listOf(section(style = cards))).size)
117
+ // Plain and condensed tiles both take a shape.
118
+ assertTrue(findings(listOf(section(style = condensed))).isEmpty())
119
+ }
120
+
121
+ @Test
122
+ fun `accessorySymbol is inert only on cards`() {
123
+ val rows = sectionStyle(accessorySymbol = "lock.fill")
124
+ val cards =
125
+ sectionStyle(
126
+ display = StyleDisplay.GRID,
127
+ gridTile = GridTile.CARD,
128
+ accessorySymbol = "lock.fill",
129
+ )
130
+ // List rows draw accessories; card elements have no slot for one.
131
+ assertTrue(findings(listOf(section(style = rows))).isEmpty())
132
+ val found = findings(listOf(section(style = cards)))
133
+ assertEquals(1, found.size)
134
+ assertTrue(found[0], found[0].contains("no accessory slot"))
135
+ }
136
+
137
+ @Test
138
+ fun `artworkRendering is never inert`() {
139
+ val stencilled =
140
+ sectionStyle(
141
+ display = StyleDisplay.GRID,
142
+ gridTile = GridTile.CARD,
143
+ artworkRendering = ArtworkRendering.STENCIL,
144
+ )
145
+ assertTrue(findings(listOf(section(style = stencilled))).isEmpty())
146
+ }
147
+
148
+ // Track-level declarations
149
+
150
+ @Test
151
+ fun `track declarations are reported per section`() {
152
+ val results =
153
+ section(
154
+ style = sectionStyle(display = StyleDisplay.GRID),
155
+ title = "Results",
156
+ children =
157
+ arrayOf(
158
+ track(style = trackStyle(cardTint = "#111")),
159
+ track(src = "https://s/b.mp3", style = trackStyle(cardTint = "#222")),
160
+ track(src = "https://s/c.mp3"),
161
+ ),
162
+ )
163
+ val found = findings(listOf(results))
164
+ assertEquals(1, found.size)
165
+ assertTrue(found[0], found[0].contains("2 tracks in section 'Results'"))
166
+ assertTrue(found[0], found[0].contains("style.cardTint"))
167
+ }
168
+
169
+ @Test
170
+ fun `a track's own shape is live in a plain grid`() {
171
+ val grid =
172
+ section(
173
+ style = sectionStyle(display = StyleDisplay.GRID),
174
+ children = arrayOf(track(style = trackStyle(imageShape = ImageShape.CIRCULAR))),
175
+ )
176
+ assertTrue(findings(listOf(grid)).isEmpty())
177
+ }
178
+
179
+ // The page block
180
+
181
+ @Test
182
+ fun `a page declaration live in one section is not reported`() {
183
+ // Card tint reaches both sections; one renders cards. Live is live.
184
+ val found =
185
+ findings(
186
+ listOf(
187
+ section(style = sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD)),
188
+ section(style = sectionStyle(display = StyleDisplay.GRID)),
189
+ ),
190
+ page = sectionStyle(cardTint = "#1e3a8a"),
191
+ )
192
+ assertTrue(found.isEmpty())
193
+ }
194
+
195
+ @Test
196
+ fun `a page declaration no section renders is reported once`() {
197
+ val found =
198
+ findings(
199
+ listOf(
200
+ section(style = sectionStyle(display = StyleDisplay.GRID), title = "A"),
201
+ section(title = "B"),
202
+ ),
203
+ page = sectionStyle(cardTint = "#1e3a8a"),
204
+ )
205
+ assertEquals(1, found.size)
206
+ assertTrue(found[0], found[0].contains("the page block"))
207
+ assertTrue(found[0], found[0].contains("no section that inherits it"))
208
+ }
209
+
210
+ @Test
211
+ fun `a section override shadows the page value`() {
212
+ // Every section declares its own tint, so the page's is never resolved — shadowed, not inert;
213
+ // the diagnostic reports what can't render, not what isn't reached.
214
+ val found =
215
+ findings(
216
+ listOf(
217
+ section(
218
+ style =
219
+ sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD, cardTint = "#000")
220
+ )
221
+ ),
222
+ page = sectionStyle(cardTint = "#1e3a8a"),
223
+ )
224
+ assertTrue(found.isEmpty())
225
+ }
226
+
227
+ @Test
228
+ fun `a page-wide grid makes section declarations live`() {
229
+ val found =
230
+ findings(
231
+ listOf(section(style = sectionStyle(cardTint = "#111"))),
232
+ page = sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD),
233
+ )
234
+ assertTrue(found.isEmpty())
235
+ }
236
+
237
+ // The positional `display`
238
+
239
+ @Test
240
+ fun `display on a playable track is inert`() {
241
+ val tracks =
242
+ section(
243
+ title = "Tracks",
244
+ children =
245
+ arrayOf(
246
+ track(style = trackStyle(display = StyleDisplay.GRID)),
247
+ browseTrack(style = trackStyle(display = StyleDisplay.GRID)),
248
+ ),
249
+ )
250
+ val found = findings(listOf(tracks))
251
+ assertEquals(1, found.size)
252
+ assertTrue(found[0], found[0].contains("1 track in section 'Tracks'"))
253
+ assertTrue(found[0], found[0].contains("style.display"))
254
+ assertTrue(found[0], found[0].contains("opens none"))
255
+ }
256
+
257
+ @Test
258
+ fun `display on a section or page is never inert`() {
259
+ val found =
260
+ findings(
261
+ listOf(
262
+ section(
263
+ style = sectionStyle(display = StyleDisplay.LIST),
264
+ children = arrayOf(browseTrack()),
265
+ )
266
+ ),
267
+ page = sectionStyle(display = StyleDisplay.GRID),
268
+ )
269
+ assertTrue(found.isEmpty())
270
+ }
271
+
272
+ @Test
273
+ fun `an unstyled page is silent`() {
274
+ assertTrue(findings(listOf(section(children = arrayOf(track(), browseTrack())))).isEmpty())
275
+ assertTrue(findings(emptyList()).isEmpty())
276
+ }
277
+ }