react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.13

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 (416) hide show
  1. package/README.md +14 -4
  2. package/android/build.gradle +20 -1
  3. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
  4. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
  5. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
  6. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
  7. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
  8. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
  9. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  10. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -7
  11. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  12. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  13. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
  14. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
  15. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
  16. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
  17. package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
  18. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  19. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  20. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
  21. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  22. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
  23. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
  24. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
  25. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  26. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
  27. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  28. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
  29. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
  30. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
  31. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
  32. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
  33. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  34. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  35. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
  36. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  37. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  38. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  39. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  40. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
  41. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  42. package/ios/Browser/BrowserConfig.swift +102 -5
  43. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  44. package/ios/Browser/BrowserManager.swift +229 -180
  45. package/ios/Browser/BrowserPathHelper.swift +44 -44
  46. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  47. package/ios/Browser/JsonModels.swift +258 -106
  48. package/ios/Browser/SearchDrillIn.swift +6 -6
  49. package/ios/Browser/SectionPresentation.swift +64 -0
  50. package/ios/Browser/SectionScope.swift +58 -24
  51. package/ios/Browser/StyleResolver.swift +58 -0
  52. package/ios/Browser/TabBarEntries.swift +9 -3
  53. package/ios/Browser/TrackIdTemplate.swift +37 -0
  54. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  55. package/ios/CarPlay/CarPlayController.swift +227 -85
  56. package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
  57. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  58. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  59. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  60. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  61. package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
  62. package/ios/Extension/Section+Copying.swift +31 -0
  63. package/ios/Extension/Track+Copying.swift +6 -12
  64. package/ios/Extension/Track+Identity.swift +18 -0
  65. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  66. package/ios/Http/HttpClient.swift +8 -2
  67. package/ios/HybridAudioBrowser.swift +166 -45
  68. package/ios/Model/NitroTypeStubs.swift +106 -21
  69. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  70. package/ios/Player/MediaIntentCriteria.swift +13 -5
  71. package/ios/Player/MediaLoader.swift +33 -9
  72. package/ios/Player/NowPlayingUpdater.swift +54 -6
  73. package/ios/Player/PlaybackCoordinator.swift +13 -6
  74. package/ios/Player/PlaybackTimer.swift +8 -2
  75. package/ios/Player/QueueManager.swift +36 -10
  76. package/ios/Player/RetryManager.swift +4 -3
  77. package/ios/Player/ShuffleOrder.swift +23 -0
  78. package/ios/Player/SleepTimerHandling.swift +13 -0
  79. package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
  80. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  81. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  82. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  83. package/ios/Tests/JsonModelsTests.swift +150 -1
  84. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  85. package/ios/Tests/QueueManagerTests.swift +72 -6
  86. package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
  87. package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
  88. package/ios/Tests/SearchDrillInTests.swift +16 -16
  89. package/ios/Tests/SectionPresentationTests.swift +90 -0
  90. package/ios/Tests/SectionScopeTests.swift +87 -55
  91. package/ios/Tests/StyleResolverTests.swift +127 -0
  92. package/ios/Tests/Support/TestTypes.swift +7 -0
  93. package/ios/Tests/TabBarEntriesTests.swift +18 -18
  94. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  95. package/ios/Tests/TrackIdentityTests.swift +20 -0
  96. package/ios/Tests/TrackSelectorTests.swift +168 -33
  97. package/ios/TrackPlayer.swift +57 -34
  98. package/ios/TrackSelector.swift +45 -30
  99. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  100. package/ios/Util/NetworkMonitor.swift +43 -26
  101. package/ios/Util/SFSymbolRenderer.swift +24 -2
  102. package/ios/Util/SVGProcessor.swift +9 -2
  103. package/lib/commonjs/AudioBrowser.js +11 -0
  104. package/lib/commonjs/AudioBrowser.js.map +1 -1
  105. package/lib/commonjs/features/browser-config.js +11 -8
  106. package/lib/commonjs/features/browser-config.js.map +1 -1
  107. package/lib/commonjs/features/browser.js +2 -2
  108. package/lib/commonjs/features/favorites.js +13 -10
  109. package/lib/commonjs/features/favorites.js.map +1 -1
  110. package/lib/commonjs/features/playback/controls.js +4 -2
  111. package/lib/commonjs/features/playback/controls.js.map +1 -1
  112. package/lib/commonjs/features/playback/progress.js +2 -1
  113. package/lib/commonjs/features/playback/progress.js.map +1 -1
  114. package/lib/commonjs/features/playback/rate.js +8 -1
  115. package/lib/commonjs/features/playback/rate.js.map +1 -1
  116. package/lib/commonjs/features/playback/state.js +2 -3
  117. package/lib/commonjs/features/playback/state.js.map +1 -1
  118. package/lib/commonjs/features/player/options.js.map +1 -1
  119. package/lib/commonjs/features/player/setup.js +3 -1
  120. package/lib/commonjs/features/player/setup.js.map +1 -1
  121. package/lib/commonjs/features/player/validateOptions.js +2 -0
  122. package/lib/commonjs/features/player/validateOptions.js.map +1 -1
  123. package/lib/commonjs/features/queue/activeTrack.js +14 -3
  124. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  125. package/lib/commonjs/features/queue/queue.js +11 -4
  126. package/lib/commonjs/features/queue/queue.js.map +1 -1
  127. package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
  128. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  129. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  130. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  131. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  132. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  133. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  134. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  135. package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
  136. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  137. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  138. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  139. package/lib/commonjs/web/browser/BrowserManager.js +144 -87
  140. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  141. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  142. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  143. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  144. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  145. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  146. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  147. package/lib/commonjs/web/browser/sections.js +89 -0
  148. package/lib/commonjs/web/browser/sections.js.map +1 -0
  149. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  150. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  151. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  152. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  153. package/lib/module/AudioBrowser.js +1 -0
  154. package/lib/module/AudioBrowser.js.map +1 -1
  155. package/lib/module/features/browser-config.js +11 -8
  156. package/lib/module/features/browser-config.js.map +1 -1
  157. package/lib/module/features/browser.js +2 -2
  158. package/lib/module/features/favorites.js +13 -10
  159. package/lib/module/features/favorites.js.map +1 -1
  160. package/lib/module/features/playback/controls.js +4 -2
  161. package/lib/module/features/playback/controls.js.map +1 -1
  162. package/lib/module/features/playback/progress.js +2 -1
  163. package/lib/module/features/playback/progress.js.map +1 -1
  164. package/lib/module/features/playback/rate.js +8 -1
  165. package/lib/module/features/playback/rate.js.map +1 -1
  166. package/lib/module/features/playback/state.js +2 -3
  167. package/lib/module/features/playback/state.js.map +1 -1
  168. package/lib/module/features/player/options.js.map +1 -1
  169. package/lib/module/features/player/setup.js +3 -1
  170. package/lib/module/features/player/setup.js.map +1 -1
  171. package/lib/module/features/player/validateOptions.js +2 -0
  172. package/lib/module/features/player/validateOptions.js.map +1 -1
  173. package/lib/module/features/queue/activeTrack.js +14 -3
  174. package/lib/module/features/queue/activeTrack.js.map +1 -1
  175. package/lib/module/features/queue/queue.js +11 -4
  176. package/lib/module/features/queue/queue.js.map +1 -1
  177. package/lib/module/utils/LazyNativeEmitter.js +21 -10
  178. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  179. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  180. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  181. package/lib/module/utils/getTrackIdentity.js +20 -0
  182. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  183. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  184. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  185. package/lib/module/web/NativeAudioBrowser.js +32 -24
  186. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  187. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  188. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  189. package/lib/module/web/browser/BrowserManager.js +144 -87
  190. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  191. package/lib/module/web/browser/FavoriteManager.js +31 -21
  192. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  193. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  194. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  195. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  196. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  197. package/lib/module/web/browser/sections.js +83 -0
  198. package/lib/module/web/browser/sections.js.map +1 -0
  199. package/lib/module/web/player/NowPlayingManager.js +4 -3
  200. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  201. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  202. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  203. package/lib/typescript/src/AudioBrowser.d.ts +1 -0
  204. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  205. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  206. package/lib/typescript/src/features/browser.d.ts +2 -2
  207. package/lib/typescript/src/features/favorites.d.ts +13 -10
  208. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  209. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  210. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  211. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  212. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  213. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  214. package/lib/typescript/src/features/playback/state.d.ts +2 -3
  215. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  216. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  217. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  218. package/lib/typescript/src/features/player/options.d.ts +21 -19
  219. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  220. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  221. package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
  222. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
  223. package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
  224. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  225. package/lib/typescript/src/features/queue/queue.d.ts +5 -2
  226. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  227. package/lib/typescript/src/types/browser-native.d.ts +2 -2
  228. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  229. package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
  230. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  231. package/lib/typescript/src/types/browser.d.ts +33 -53
  232. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  233. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
  234. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  235. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  236. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  237. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  238. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  239. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  240. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  241. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
  242. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  243. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  244. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  245. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  246. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  247. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  248. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  249. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  250. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  251. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  252. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  253. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  254. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  255. 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
  256. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  257. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
  258. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  259. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  260. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  261. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  262. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +33 -14
  263. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  264. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  265. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  266. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +22 -10
  267. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  268. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
  269. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
  270. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  271. package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
  272. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  273. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  274. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
  275. package/nitrogen/generated/android/c++/JSection.hpp +120 -0
  276. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  277. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  278. package/nitrogen/generated/android/c++/JTrack.hpp +23 -48
  279. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  280. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  281. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  282. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  283. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  284. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  285. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  286. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  287. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  288. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
  289. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  290. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  291. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
  292. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -6
  293. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  294. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  295. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
  296. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  297. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
  298. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
  299. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
  300. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  301. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  302. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  303. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  304. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  305. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  306. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
  307. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  308. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  309. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
  310. package/nitrogen/generated/ios/swift/Section.swift +100 -0
  311. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  312. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  313. package/nitrogen/generated/ios/swift/Track.swift +20 -73
  314. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  315. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  316. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  317. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  318. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  319. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  320. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  321. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
  322. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  323. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  324. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
  325. package/nitrogen/generated/shared/c++/Section.hpp +106 -0
  326. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  327. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  328. package/nitrogen/generated/shared/c++/Track.hpp +15 -31
  329. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  330. package/package.json +7 -2
  331. package/src/AudioBrowser.ts +1 -0
  332. package/src/features/browser-config.test.ts +21 -6
  333. package/src/features/browser-config.ts +11 -8
  334. package/src/features/browser.test.ts +1 -1
  335. package/src/features/browser.ts +2 -2
  336. package/src/features/favorites.ts +13 -10
  337. package/src/features/hook-wiring.test.ts +63 -15
  338. package/src/features/playback/controls.ts +4 -2
  339. package/src/features/playback/progress.ts +2 -1
  340. package/src/features/playback/rate.ts +8 -1
  341. package/src/features/playback/state.ts +2 -3
  342. package/src/features/playback/trackPlaybackTime.ts +1 -1
  343. package/src/features/player/options.ts +27 -19
  344. package/src/features/player/setup.test.ts +12 -3
  345. package/src/features/player/setup.ts +3 -1
  346. package/src/features/player/validateOptions.ts +2 -0
  347. package/src/features/queue/activeTrack.ts +14 -3
  348. package/src/features/queue/queue.ts +11 -4
  349. package/src/nitro-stub-drift.test.ts +40 -0
  350. package/src/public-surface.test.ts +40 -0
  351. package/src/specs/audio-browser.nitro.ts +4 -3
  352. package/src/style-resolution.test.ts +210 -0
  353. package/src/types/browser-native.ts +2 -2
  354. package/src/types/browser-nodes.ts +377 -135
  355. package/src/types/browser.ts +34 -55
  356. package/src/utils/LazyNativeEmitter.ts +20 -10
  357. package/src/utils/NativeUpdatedValue.ts +29 -13
  358. package/src/utils/getTrackIdentity.ts +20 -0
  359. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  360. package/src/web/NativeAudioBrowser.ts +37 -22
  361. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  362. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  363. package/src/web/browser/BrowserManager.ts +176 -95
  364. package/src/web/browser/FavoriteManager.test.ts +84 -0
  365. package/src/web/browser/FavoriteManager.ts +28 -23
  366. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  367. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  368. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  369. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  370. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  371. package/src/web/browser/sections.test.ts +91 -0
  372. package/src/web/browser/sections.ts +72 -0
  373. package/src/web/player/NowPlayingManager.ts +4 -3
  374. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  375. package/src/web/util/BrowserPathHelper.ts +61 -13
  376. package/website/guide/android-auto.md +114 -0
  377. package/website/guide/android-certificates.md +409 -0
  378. package/website/guide/artwork.md +255 -0
  379. package/website/guide/audio-output.md +135 -0
  380. package/website/guide/automotive.md +86 -0
  381. package/website/guide/basic-usage.md +151 -0
  382. package/website/guide/battery.md +115 -0
  383. package/website/guide/browser.md +581 -0
  384. package/website/guide/carplay.md +620 -0
  385. package/website/guide/configuration.md +173 -0
  386. package/website/guide/equalizer.md +231 -0
  387. package/website/guide/errors.md +310 -0
  388. package/website/guide/favorites.md +154 -0
  389. package/website/guide/gate.md +143 -0
  390. package/website/guide/getting-started.md +46 -0
  391. package/website/guide/hooks.md +238 -0
  392. package/website/guide/live-streams.md +125 -0
  393. package/website/guide/metadata.md +239 -0
  394. package/website/guide/migrating-from-track-player.md +244 -0
  395. package/website/guide/native-callback-fetch.md +127 -0
  396. package/website/guide/network.md +86 -0
  397. package/website/guide/now-playing.md +175 -0
  398. package/website/guide/playback.md +328 -0
  399. package/website/guide/queue.md +300 -0
  400. package/website/guide/remote-controls.md +256 -0
  401. package/website/guide/search.md +255 -0
  402. package/website/guide/sleep-timer.md +268 -0
  403. package/website/guide/track.md +246 -0
  404. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  405. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  406. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  407. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  408. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  409. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  410. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  411. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  412. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  413. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  414. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  415. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  416. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
package/README.md CHANGED
@@ -63,7 +63,7 @@ import AudioBrowser from 'react-native-audio-browser'
63
63
  await AudioBrowser.setupPlayer()
64
64
 
65
65
  AudioBrowser.configureBrowser({
66
- tabs: [{ title: 'Browse', url: '/browse' }],
66
+ tabs: [{ title: 'Browse', path: '/browse' }],
67
67
  routes: {
68
68
  // Every path under /browse is fetched from your API, on demand.
69
69
  '/browse/**': { baseUrl: 'https://api.example.com' }
@@ -71,15 +71,15 @@ AudioBrowser.configureBrowser({
71
71
  })
72
72
  ```
73
73
 
74
- Each route returns a page of children — a child with a `url` is browsable (open it for more), one with a `src` is a playable track (plus optional `artist`, `artwork`, …):
74
+ Each route returns a page of children — a child with a `path` is browsable (open it for more), one with a `src` is a playable track (plus optional `artist`, `artwork`, …):
75
75
 
76
76
  ```jsonc
77
77
  // GET https://api.example.com/browse/jazz →
78
78
  {
79
- "url": "/browse/jazz",
79
+ "path": "/browse/jazz",
80
80
  "title": "Jazz",
81
81
  "children": [
82
- { "title": "Smooth Floret FM", "url": "/browse/jazz/floret-fm" }, // url → open for children
82
+ { "title": "Smooth Floret FM", "path": "/browse/jazz/floret-fm" }, // path → open for children
83
83
  { "title": "The Stalk 88.5", "src": "https://stream.example.com/stalk.mp3" } // src → play this stream
84
84
  ]
85
85
  }
@@ -174,6 +174,16 @@ Full guides and the complete [API Reference](https://audiobrowser.dev/api/) live
174
174
 
175
175
  The example app in [`apps/example-native`](https://github.com/radio-garden/react-native-audio-browser/tree/main/apps/example-native) browses archive.org's audio collection — in-app, on CarPlay, and on Android Auto — with search, favorites, and the gate.
176
176
 
177
+ ### For coding agents
178
+
179
+ Every page is also served as raw Markdown — append `.md` to any URL, e.g.
180
+ [`audiobrowser.dev/guide/queue.md`](https://audiobrowser.dev/guide/queue.md).
181
+
182
+ - [`/llms.txt`](https://audiobrowser.dev/llms.txt) — an index of every page with a one-line summary, to pick what to read.
183
+ - [`/llms-full.txt`](https://audiobrowser.dev/llms-full.txt) — every guide in one file.
184
+
185
+ The same guide Markdown ships in the package, so it can be read straight off disk without a network fetch: `node_modules/react-native-audio-browser/website/guide/`.
186
+
177
187
  ## Support
178
188
 
179
189
  Questions or bugs? [Open an issue](https://github.com/radio-garden/react-native-audio-browser/issues). Release notes live in the [changelog](CHANGELOG.md).
@@ -158,6 +158,13 @@ repositories {
158
158
  google()
159
159
  }
160
160
 
161
+ // Mockito's inline mock maker needs a Byte Buddy agent in the running JVM. Left to itself it
162
+ // installs one through the JVM attach mechanism, which binds a unix socket under $TMPDIR —
163
+ // denied inside sandboxed shells, and disabled by default on JDK 24+. Resolving the agent as its
164
+ // own configuration lets the test JVM pre-load it with -javaagent, so no attach is needed.
165
+ configurations {
166
+ mockitoAgent
167
+ }
161
168
 
162
169
  dependencies {
163
170
  // For < 0.71, this will be from the local maven repo
@@ -210,14 +217,26 @@ dependencies {
210
217
  implementation("io.coil-kt.coil3:coil-network-okhttp:3.3.0")
211
218
 
212
219
  // Testing dependencies
220
+ def mockitoVersion = "5.8.0"
213
221
  testImplementation 'junit:junit:4.13.2'
214
- testImplementation 'org.mockito:mockito-core:5.8.0'
222
+ testImplementation "org.mockito:mockito-core:${mockitoVersion}"
223
+ // Resolved transitively so the agent jar always matches the Byte Buddy that Mockito uses.
224
+ mockitoAgent "org.mockito:mockito-core:${mockitoVersion}"
215
225
  testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
216
226
  testImplementation 'org.robolectric:robolectric:4.11.1'
217
227
 
218
228
  api 'com.jakewharton.timber:timber:5.0.1'
219
229
  }
220
230
 
231
+ tasks.withType(Test).configureEach {
232
+ def agentJar = configurations.mockitoAgent.find { it.name.startsWith("byte-buddy-agent") }
233
+ if (agentJar == null) {
234
+ throw new GradleException("No byte-buddy-agent jar in the mockitoAgent configuration; " +
235
+ "Mockito would fall back to runtime self-attach. Check the mockito-core coordinates.")
236
+ }
237
+ jvmArgs "-javaagent:${agentJar}"
238
+ }
239
+
221
240
  if (isNewArchitectureEnabled()) {
222
241
  react {
223
242
  jsRootDir = file("../src/")
@@ -20,9 +20,11 @@ import com.audiobrowser.browser.CallbackException
20
20
  import com.audiobrowser.browser.ContentNotFoundException
21
21
  import com.audiobrowser.browser.HttpStatusException
22
22
  import com.audiobrowser.browser.NetworkException
23
+ import com.audiobrowser.browser.flattenedChildren
23
24
  import com.audiobrowser.browser.handleTrackLoad
24
25
  import com.audiobrowser.browser.resolveMediaUrl
25
26
  import com.audiobrowser.extension.NumberExt.Companion.toSeconds
27
+ import com.audiobrowser.extension.indexOfTappedTrack
26
28
  import com.audiobrowser.model.PlayerSetupOptions
27
29
  import com.audiobrowser.model.PlayerUpdateOptions
28
30
  import com.audiobrowser.player.Player
@@ -35,6 +37,7 @@ import com.audiobrowser.util.SystemVolumeMonitor
35
37
  import com.facebook.proguard.annotations.DoNotStrip
36
38
  import com.google.common.util.concurrent.ListenableFuture
37
39
  import com.margelo.nitro.NitroModules
40
+ import com.margelo.nitro.audiobrowser.ArtworkRequestConfig
38
41
  import com.margelo.nitro.audiobrowser.BatteryOptimizationStatus
39
42
  import com.margelo.nitro.audiobrowser.BatteryOptimizationStatusChangedEvent
40
43
  import com.margelo.nitro.audiobrowser.BatteryWarningPendingChangedEvent
@@ -50,6 +53,7 @@ import com.margelo.nitro.audiobrowser.HybridAudioBrowserSpec
50
53
  import com.margelo.nitro.audiobrowser.MediaRequestConfig
51
54
  import com.margelo.nitro.audiobrowser.NativeBrowserConfiguration
52
55
  import com.margelo.nitro.audiobrowser.NativeGateRequest
56
+ import com.margelo.nitro.audiobrowser.NativeRouteEntry
53
57
  import com.margelo.nitro.audiobrowser.NativeSetupPlayerOptions
54
58
  import com.margelo.nitro.audiobrowser.NativeUpdateOptions
55
59
  import com.margelo.nitro.audiobrowser.NavigationError
@@ -82,6 +86,7 @@ import com.margelo.nitro.audiobrowser.SleepTimer
82
86
  import com.margelo.nitro.audiobrowser.TimedMetadata
83
87
  import com.margelo.nitro.audiobrowser.Track
84
88
  import com.margelo.nitro.audiobrowser.TrackMetadata
89
+ import com.margelo.nitro.audiobrowser.TransformableRequestConfig
85
90
  import com.margelo.nitro.core.Promise
86
91
  import java.util.concurrent.TimeUnit
87
92
  import java.util.concurrent.atomic.AtomicReference
@@ -121,6 +126,12 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
121
126
  // Only when still ours: on a reload the replacement may already own the slot.
122
127
  carConnectionTarget.compareAndSet(onCarConnectedChanged, null)
123
128
 
129
+ // The runtime behind the config's JS functions is going away, but anything still holding
130
+ // this instance (an in-flight browse coroutine, the running player) can invoke them after
131
+ // it's gone — each invoke then throws instead of answering, so browse takes its error path
132
+ // rather than its fallback.
133
+ dropStaleJSCallbacks()
134
+
124
135
  handler.post {
125
136
  connectedService?.let { service ->
126
137
  // Identity-guarded: both instances bind to the same singleton Service, so a late dispose
@@ -129,6 +140,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
129
140
  if (service.player.browser === this) {
130
141
  service.player.browser = null
131
142
  service.player.forgetBrowserRegistration()
143
+ // The formatter wraps a JS callback from this instance's runtime (installed by our
144
+ // setup(); browser still being ours means no replacement has run setup yet). Without
145
+ // this, every track change until the next setup() throws into the updater's fallback.
146
+ service.player.nowPlaying.formatter = null
132
147
  }
133
148
  if (service.onBatteryWarningPendingChanged === onBatteryWarningPending) {
134
149
  service.onBatteryWarningPendingChanged = null
@@ -152,6 +167,20 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
152
167
  super.dispose()
153
168
  }
154
169
 
170
+ /**
171
+ * Demotes the config to data-only (deep strip of every JS function field) and reverts the JS
172
+ * callbacks stored outside the config tree ([resolveGate]; the now-playing formatter is cleared
173
+ * in [dispose]'s service block, identity-guarded). Stale paths then degrade the same way as a
174
+ * config that never had callbacks — static request/browse layers, default error copy, fail-closed
175
+ * gate — instead of throwing into the error path. Mirrors iOS `dropStaleJSCallbacks`, where the
176
+ * same invoke is fatal rather than a throw.
177
+ */
178
+ private fun dropStaleJSCallbacks() {
179
+ _configuration = _configuration.strippingJSCallbacks()
180
+ browserManager.config = buildConfig()
181
+ resolveGate = defaultResolveGate
182
+ }
183
+
155
184
  private val mainScope = MainScope()
156
185
  private var navigationJob: Job? = null
157
186
  private val handler = Handler(Looper.getMainLooper())
@@ -175,7 +204,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
175
204
  handleTrackLoad = null,
176
205
  androidControllerOfflineError = null,
177
206
  carPlayLoadingTitle = null,
178
- resolveAlbumUrl = null,
207
+ resolveAlbumPath = null,
179
208
  formatNavigationError = null,
180
209
  )
181
210
 
@@ -220,14 +249,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
220
249
  override var onFormattedNavigationError: (FormattedNavigationError?) -> Unit = {}
221
250
 
222
251
  // MARK: Gate callbacks (native→JS; set from JS, native CALLS them)
223
- // Default resolver: only reachable in the init window where the native side has recorded
224
- // hasResolver=true but JS hasn't yet assigned the real `resolveGate` slot. It DENIES by default
252
+ // Default resolver: only reachable while hasResolver=true but no live runtime has the real
253
+ // `resolveGate` bound — the init window before JS assigns it, or after [dispose] reverts a dead
254
+ // runtime's resolver. It DENIES by default
225
255
  // (gated=true) so an active gate never serves content during that window — same fail-closed
226
256
  // direction as the resolver-error path in [gateDecision]. The static-gate fast path skips it
227
257
  // entirely once a resolver-less gate is set.
228
- override var resolveGate: (request: NativeGateRequest) -> Promise<Promise<GateDecision>> = {
229
- Promise.resolved(Promise.resolved(GateDecision(gated = true, gate = null)))
230
- }
258
+ override var resolveGate: (request: NativeGateRequest) -> Promise<Promise<GateDecision>> =
259
+ defaultResolveGate
231
260
  override var onGate: (event: GateEvent) -> Unit = {}
232
261
 
233
262
  // MARK: Player callbacks
@@ -373,8 +402,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
373
402
  browserManager.config = buildConfig()
374
403
  val tabs = browserManager.queryTabs()
375
404
  if (tabs.isNotEmpty()) {
376
- Timber.d("Using first tab as default path: ${tabs[0].url}")
377
- tabs[0].url
405
+ Timber.d("Using first tab as default path: ${tabs[0].path}")
406
+ tabs[0].path
378
407
  } else {
379
408
  Timber.d("Using root path as default: /")
380
409
  "/"
@@ -612,26 +641,38 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
612
641
  }
613
642
 
614
643
  override fun navigateTrack(track: Track) {
644
+ // A disabled track is unavailable — it never plays, whichever surface or
645
+ // stale resume path delivered the selection (Track.disabled; mirrors iOS
646
+ // TrackSelector.select). Refused before any state changes: an inert tap
647
+ // must not clear an unrelated navigation error or cancel an in-flight
648
+ // navigation.
649
+ if (track.disabled == true) {
650
+ Timber.d("Ignoring selection of disabled track: ${track.title}")
651
+ return
652
+ }
653
+
615
654
  clearNavigationError()
616
655
 
617
656
  // Cancel previous navigation to avoid race conditions
618
657
  navigationJob?.cancel()
619
658
 
620
- val url = track.url
659
+ val path = track.path
621
660
  navigationJob =
622
661
  mainScope.launch {
623
662
  try {
624
663
  when {
625
- // Check if this is a contextual URL (playable-only track with queue context)
626
- url != null && BrowserPathHelper.isContextual(url) -> {
627
- Timber.d("Navigating to contextual track URL: $url")
664
+ // Check if this is a contextual path (playable-only track with queue context)
665
+ path != null && BrowserPathHelper.isContextual(path) -> {
666
+ Timber.d("Navigating to contextual track path: $path")
628
667
 
629
- val parentPath = BrowserPathHelper.stripTrackId(url)
630
- val trackId = BrowserPathHelper.extractTrackId(url)
668
+ val parentPath = BrowserPathHelper.stripTrackId(path)
669
+ val trackId = BrowserPathHelper.extractTrackId(path)
631
670
 
632
- // Check if queue already came from this parent path - just skip to the track
671
+ // Check if queue already came from this parent path - just skip
672
+ // to the tapped surface (exact path first, identity for
673
+ // index-less paths — see indexOfTappedTrack)
633
674
  if (trackId != null && parentPath == player.queueSourcePath) {
634
- val index = player.tracks.indexOfFirst { it.src == trackId }
675
+ val index = player.tracks.indexOfTappedTrack(path, trackId)
635
676
  if (index >= 0) {
636
677
  Timber.d("Queue already from $parentPath, skipping to index $index")
637
678
  handleTrackLoad(
@@ -649,8 +690,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
649
690
  }
650
691
  }
651
692
 
652
- // Expand the queue from the contextual URL
653
- val expanded = browserManager.expandQueueFromContextualUrl(url)
693
+ // Expand the queue from the contextual path
694
+ val expanded = browserManager.expandQueueFromContextualPath(path)
654
695
 
655
696
  if (expanded != null) {
656
697
  val (tracks, startIndex) = expanded
@@ -685,9 +726,9 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
685
726
  }
686
727
  }
687
728
  // Navigate to browsable track to show browsing UI
688
- url != null -> {
689
- Timber.d("Navigating to browsable track: $url")
690
- browserManager.navigate(url)
729
+ path != null -> {
730
+ Timber.d("Navigating to browsable track: $path")
731
+ browserManager.navigate(path)
691
732
  }
692
733
  // If track is playable (has src), load it into player
693
734
  track.src != null -> {
@@ -702,13 +743,13 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
702
743
  )
703
744
  }
704
745
  else -> {
705
- throw IllegalArgumentException("Track must have either an 'url' or an 'src' property")
746
+ throw IllegalArgumentException("Track must have either a 'path' or an 'src' property")
706
747
  }
707
748
  }
708
749
  } catch (e: CancellationException) {
709
750
  throw e // Rethrow to properly cancel
710
751
  } catch (e: Exception) {
711
- handleBrowserException(e, url ?: track.src ?: "", "navigating to track: ${track.title}")
752
+ handleBrowserException(e, path ?: track.src ?: "", "navigating to track: ${track.title}")
712
753
  }
713
754
  }
714
755
  }
@@ -717,7 +758,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
717
758
  return Promise.async(mainScope) {
718
759
  Timber.d("Searching for: $query")
719
760
  val searchResults = browserManager.search(query)
720
- searchResults.children ?: emptyArray()
761
+ // Flattened, not `children`: a resolved page carries its rows in
762
+ // `sections` (ADR 0010) and search builds exactly one untitled section,
763
+ // so `children` is always null here and this returned nothing at all.
764
+ searchResults.flattenedChildren?.toTypedArray() ?: emptyArray()
721
765
  }
722
766
  }
723
767
 
@@ -1027,12 +1071,15 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1027
1071
  player.setQueue(tracks, startIndex?.toInt() ?: 0, ((startPosition ?: 0.0) * 1000).toLong())
1028
1072
  }
1029
1073
 
1030
- override fun setActiveTrackFavorited(favorited: Boolean) = runBlockingOnMain {
1074
+ override fun setActiveTrackFavorited(favorited: Boolean): Unit = runBlockingOnMain {
1075
+ // Applied-result is for MediaSession honesty; the JS API stays fire-and-forget.
1031
1076
  player.setActiveTrackFavorited(favorited)
1077
+ Unit
1032
1078
  }
1033
1079
 
1034
- override fun toggleActiveTrackFavorited() = runBlockingOnMain {
1080
+ override fun toggleActiveTrackFavorited(): Unit = runBlockingOnMain {
1035
1081
  player.toggleActiveTrackFavorited()
1082
+ Unit
1036
1083
  }
1037
1084
 
1038
1085
  override fun getQueue(): Array<Track> = runBlockingOnMain { player.tracks }
@@ -1494,6 +1541,11 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1494
1541
  current.getAndSet(null)?.dispose()
1495
1542
  }
1496
1543
 
1544
+ /** Shared by the [resolveGate] initializer and [dispose], which reverts to it. */
1545
+ private val defaultResolveGate: (NativeGateRequest) -> Promise<Promise<GateDecision>> = {
1546
+ Promise.resolved(Promise.resolved(GateDecision(gated = true, gate = null)))
1547
+ }
1548
+
1497
1549
  private fun startCarConnectionObserver(context: Context, handler: Handler) {
1498
1550
  // Posted to main: LiveData observation is main-thread only, and the
1499
1551
  // single-start guard is then only ever touched from one thread.
@@ -1510,3 +1562,40 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1510
1562
  }
1511
1563
  }
1512
1564
  }
1565
+
1566
+ // MARK: Deep JS-callback stripping (used by dispose)
1567
+ // Functions live at every level of the config, not just the top; each type's strip nils its own.
1568
+
1569
+ private fun NativeBrowserConfiguration.strippingJSCallbacks() =
1570
+ copy(
1571
+ requestResolver = null,
1572
+ browseResolver = null,
1573
+ handleTrackLoad = null,
1574
+ resolveAlbumPath = null,
1575
+ formatNavigationError = null,
1576
+ request = request?.strippingJSCallbacks(),
1577
+ browse = browse?.strippingJSCallbacks(),
1578
+ media = media?.strippingJSCallbacks(),
1579
+ artwork = artwork?.strippingJSCallbacks(),
1580
+ nowPlayingArtwork = nowPlayingArtwork?.strippingJSCallbacks(),
1581
+ routes = routes?.map { it.strippingJSCallbacks() }?.toTypedArray(),
1582
+ )
1583
+
1584
+ private fun TransformableRequestConfig.strippingJSCallbacks() =
1585
+ copy(transform = null, transformSync = null)
1586
+
1587
+ private fun MediaRequestConfig.strippingJSCallbacks() =
1588
+ copy(resolve = null, resolveSync = null, transform = null, transformSync = null)
1589
+
1590
+ private fun ArtworkRequestConfig.strippingJSCallbacks() =
1591
+ copy(resolve = null, resolveSync = null, transform = null, transformSync = null)
1592
+
1593
+ private fun NativeRouteEntry.strippingJSCallbacks() =
1594
+ copy(
1595
+ browseCallback = null,
1596
+ browseConfig = browseConfig?.strippingJSCallbacks(),
1597
+ searchCallback = null,
1598
+ searchConfig = searchConfig?.strippingJSCallbacks(),
1599
+ media = media?.strippingJSCallbacks(),
1600
+ artwork = artwork?.strippingJSCallbacks(),
1601
+ )