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.
- package/README.md +14 -4
- package/android/build.gradle +20 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
- package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +229 -180
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +258 -106
- package/ios/Browser/SearchDrillIn.swift +6 -6
- package/ios/Browser/SectionPresentation.swift +64 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +58 -0
- package/ios/Browser/TabBarEntries.swift +9 -3
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +227 -85
- package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
- package/ios/Extension/Section+Copying.swift +31 -0
- package/ios/Extension/Track+Copying.swift +6 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +166 -45
- package/ios/Model/NitroTypeStubs.swift +106 -21
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Player/MediaIntentCriteria.swift +13 -5
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +36 -10
- package/ios/Player/RetryManager.swift +4 -3
- package/ios/Player/ShuffleOrder.swift +23 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +150 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/QueueManagerTests.swift +72 -6
- package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +90 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +127 -0
- package/ios/Tests/Support/TestTypes.swift +7 -0
- package/ios/Tests/TabBarEntriesTests.swift +18 -18
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +57 -34
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/AudioBrowser.js +11 -0
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/browser-config.js +11 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +2 -2
- package/lib/commonjs/features/favorites.js +13 -10
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/progress.js +2 -1
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +2 -3
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +3 -1
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +2 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js +14 -3
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +11 -4
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +144 -87
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +1 -0
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/browser-config.js +11 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +2 -2
- package/lib/module/features/favorites.js +13 -10
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/progress.js +2 -1
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/features/playback/state.js +2 -3
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +3 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +2 -0
- package/lib/module/features/player/validateOptions.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js +14 -3
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +11 -4
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +21 -10
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +32 -24
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +144 -87
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +2 -2
- package/lib/typescript/src/features/favorites.d.ts +13 -10
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +2 -3
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +21 -19
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +5 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +33 -53
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
- 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
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +33 -14
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +22 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
- package/nitrogen/generated/android/c++/JSection.hpp +120 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +23 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
- package/nitrogen/generated/ios/swift/Section.swift +100 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +20 -73
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
- package/nitrogen/generated/shared/c++/Section.hpp +106 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +15 -31
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +7 -2
- package/src/AudioBrowser.ts +1 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +11 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +2 -2
- package/src/features/favorites.ts +13 -10
- package/src/features/hook-wiring.test.ts +63 -15
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/progress.ts +2 -1
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +2 -3
- package/src/features/playback/trackPlaybackTime.ts +1 -1
- package/src/features/player/options.ts +27 -19
- package/src/features/player/setup.test.ts +12 -3
- package/src/features/player/setup.ts +3 -1
- package/src/features/player/validateOptions.ts +2 -0
- package/src/features/queue/activeTrack.ts +14 -3
- package/src/features/queue/queue.ts +11 -4
- package/src/nitro-stub-drift.test.ts +40 -0
- package/src/public-surface.test.ts +40 -0
- package/src/specs/audio-browser.nitro.ts +4 -3
- package/src/style-resolution.test.ts +210 -0
- package/src/types/browser-native.ts +2 -2
- package/src/types/browser-nodes.ts +377 -135
- package/src/types/browser.ts +34 -55
- package/src/utils/LazyNativeEmitter.ts +20 -10
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.ts +37 -22
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/browser/BrowserManager.ts +176 -95
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +246 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
|
@@ -8,14 +8,19 @@ import java.net.URLEncoder
|
|
|
8
8
|
* Utility for handling browser paths and contextual URLs in the media browser system.
|
|
9
9
|
*
|
|
10
10
|
* Handles two types of special paths:
|
|
11
|
-
* 1. System paths (prefixed with `/__`):
|
|
11
|
+
* 1. System paths (prefixed with `/__`): root, search, offline, error, and gate paths
|
|
12
12
|
* 2. Contextual URLs: Embed parent context in track identifiers for Media3 integration
|
|
13
13
|
*
|
|
14
|
-
* Contextual URL format: `{parentPath}?__trackId={
|
|
15
|
-
*
|
|
14
|
+
* Contextual URL format: `{parentPath}?__trackId={trackIdentity}&__index={childIndex}` (the
|
|
15
|
+
* identity is the track's `id` when non-blank, else its `src`) Example:
|
|
16
|
+
* "/library/radio?__trackId=song.mp3&__index=2"
|
|
17
|
+
*
|
|
18
|
+
* `__trackId` is the identity check; `__index` (the child's position on the page at stamp time) is
|
|
19
|
+
* only a tie-breaker between surfaces that carry the same identity — a stale index never selects a
|
|
20
|
+
* different track.
|
|
16
21
|
*
|
|
17
22
|
* This allows:
|
|
18
|
-
* - Media3 to reference playable-only tracks (tracks with `src` but no `
|
|
23
|
+
* - Media3 to reference playable-only tracks (tracks with `src` but no `path`)
|
|
19
24
|
* - Cache lookup to work consistently
|
|
20
25
|
* - Parent context to be preserved for queue restoration
|
|
21
26
|
*/
|
|
@@ -23,9 +28,6 @@ object BrowserPathHelper {
|
|
|
23
28
|
/** Root path for media browsing */
|
|
24
29
|
const val ROOT_PATH = "/__root"
|
|
25
30
|
|
|
26
|
-
/** Recent media path for playback resumption */
|
|
27
|
-
const val RECENT_PATH = "/__recent"
|
|
28
|
-
|
|
29
31
|
/** Search path prefix (full path is /__search?q=query) */
|
|
30
32
|
const val SEARCH_PATH_PREFIX = "/__search"
|
|
31
33
|
|
|
@@ -41,9 +43,12 @@ object BrowserPathHelper {
|
|
|
41
43
|
// Query parameter name for contextual track identifiers
|
|
42
44
|
private const val CONTEXTUAL_TRACK_PARAM = "__trackId"
|
|
43
45
|
|
|
46
|
+
// Query parameter name for the tapped child's page position (tie-breaker)
|
|
47
|
+
private const val CONTEXTUAL_INDEX_PARAM = "__index"
|
|
48
|
+
|
|
44
49
|
/** Check if a path is a special system path (not a regular navigation path) */
|
|
45
50
|
fun isSpecialPath(path: String): Boolean {
|
|
46
|
-
return path == ROOT_PATH || path
|
|
51
|
+
return path == ROOT_PATH || path.startsWith("$SEARCH_PATH_PREFIX?")
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/** Create a search path for a given query */
|
|
@@ -63,13 +68,13 @@ object BrowserPathHelper {
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
/**
|
|
66
|
-
* Strips the __trackId
|
|
67
|
-
* contextual, returns it unchanged.
|
|
71
|
+
* Strips the contextual parameters (__trackId and __index) from a contextual URL to get the
|
|
72
|
+
* parent path. If the URL is not contextual, returns it unchanged.
|
|
68
73
|
*
|
|
69
74
|
* @param url The URL to process
|
|
70
|
-
* @return The URL without the
|
|
75
|
+
* @return The URL without the contextual parameters
|
|
71
76
|
*
|
|
72
|
-
* Example: "/library/radio?__trackId=song.mp3" → "/library/radio" Example:
|
|
77
|
+
* Example: "/library/radio?__trackId=song.mp3&__index=2" → "/library/radio" Example:
|
|
73
78
|
* "/search?q=jazz&__trackId=song.mp3" → "/search?q=jazz"
|
|
74
79
|
*/
|
|
75
80
|
fun stripTrackId(url: String): String {
|
|
@@ -79,13 +84,13 @@ object BrowserPathHelper {
|
|
|
79
84
|
|
|
80
85
|
val uri = url.toUri()
|
|
81
86
|
|
|
82
|
-
// Build URL preserving everything except
|
|
87
|
+
// Build URL preserving everything except the contextual parameters
|
|
83
88
|
val builder = uri.buildUpon()
|
|
84
89
|
builder.clearQuery()
|
|
85
90
|
|
|
86
|
-
// Re-add all query params except __trackId
|
|
91
|
+
// Re-add all query params except __trackId and __index
|
|
87
92
|
uri.queryParameterNames.forEach { paramName ->
|
|
88
|
-
if (paramName != CONTEXTUAL_TRACK_PARAM) {
|
|
93
|
+
if (paramName != CONTEXTUAL_TRACK_PARAM && paramName != CONTEXTUAL_INDEX_PARAM) {
|
|
89
94
|
uri.getQueryParameters(paramName).forEach { value ->
|
|
90
95
|
builder.appendQueryParameter(paramName, value)
|
|
91
96
|
}
|
|
@@ -96,19 +101,21 @@ object BrowserPathHelper {
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
/**
|
|
99
|
-
* Builds a contextual URL by appending a track identifier
|
|
100
|
-
* query parameters correctly.
|
|
104
|
+
* Builds a contextual URL by appending a track identifier — and optionally the tapped child's
|
|
105
|
+
* page position — to a parent path. Handles existing query parameters correctly.
|
|
101
106
|
*
|
|
102
107
|
* @param parentPath The parent container path
|
|
103
|
-
* @param trackId The track
|
|
104
|
-
* @
|
|
108
|
+
* @param trackId The track identity (`id` when non-blank, else `src` — see Track.identity)
|
|
109
|
+
* @param index The child's position on the page at stamp time (tie-breaker)
|
|
110
|
+
* @return A contextual URL combining parent path, track ID, and index
|
|
105
111
|
*
|
|
106
|
-
* Example: build("/library", "song.mp3") → "/library?__trackId=song.mp3" Example:
|
|
112
|
+
* Example: build("/library", "song.mp3", 2) → "/library?__trackId=song.mp3&__index=2" Example:
|
|
107
113
|
* build("/search?q=jazz", "song.mp3") → "/search?q=jazz&__trackId=song.mp3"
|
|
108
114
|
*/
|
|
109
|
-
fun build(parentPath: String, trackId: String): String {
|
|
115
|
+
fun build(parentPath: String, trackId: String, index: Int? = null): String {
|
|
110
116
|
val separator = if (parentPath.contains('?')) '&' else '?'
|
|
111
|
-
|
|
117
|
+
val indexParam = if (index != null) "&$CONTEXTUAL_INDEX_PARAM=$index" else ""
|
|
118
|
+
return "$parentPath$separator$CONTEXTUAL_TRACK_PARAM=${Uri.encode(trackId)}$indexParam"
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
/**
|
|
@@ -129,6 +136,21 @@ object BrowserPathHelper {
|
|
|
129
136
|
return uri.getQueryParameter(CONTEXTUAL_TRACK_PARAM)
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Extracts the stamped page index from a contextual URL, or null when the URL is not contextual
|
|
141
|
+
* or carries no (valid, non-negative) index.
|
|
142
|
+
*
|
|
143
|
+
* Example: "/library/radio?__trackId=song.mp3&__index=2" → 2
|
|
144
|
+
*/
|
|
145
|
+
fun extractIndex(path: String): Int? {
|
|
146
|
+
if (!isContextual(path)) {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
val raw = Uri.parse(path).getQueryParameter(CONTEXTUAL_INDEX_PARAM) ?: return null
|
|
151
|
+
return raw.toIntOrNull()?.takeIf { it >= 0 }
|
|
152
|
+
}
|
|
153
|
+
|
|
132
154
|
/**
|
|
133
155
|
* Combines a base URL with a path, ensuring proper slash handling.
|
|
134
156
|
*
|
|
@@ -158,22 +180,4 @@ object BrowserPathHelper {
|
|
|
158
180
|
val normalizedPath = path.trimStart('/')
|
|
159
181
|
return "$normalizedBase$normalizedPath"
|
|
160
182
|
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* True if [segment] appears in [path] as a complete path segment — bounded on the left by the
|
|
164
|
-
* string start or `/`, and on the right by the string end or one of `/`, `?`, `#`.
|
|
165
|
-
*/
|
|
166
|
-
fun containsSegment(path: String, segment: String): Boolean {
|
|
167
|
-
if (segment.isEmpty()) return false
|
|
168
|
-
var from = 0
|
|
169
|
-
while (true) {
|
|
170
|
-
val idx = path.indexOf(segment, from)
|
|
171
|
-
if (idx < 0) return false
|
|
172
|
-
val beforeOk = idx == 0 || path[idx - 1] == '/'
|
|
173
|
-
val end = idx + segment.length
|
|
174
|
-
val afterOk = end == path.length || path[end] == '/' || path[end] == '?' || path[end] == '#'
|
|
175
|
-
if (beforeOk && afterOk) return true
|
|
176
|
-
from = idx + 1
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
183
|
}
|
|
@@ -2,30 +2,31 @@ package com.audiobrowser.util
|
|
|
2
2
|
|
|
3
3
|
import android.os.Bundle
|
|
4
4
|
import androidx.media.utils.MediaConstants
|
|
5
|
-
import com.margelo.nitro.audiobrowser.
|
|
5
|
+
import com.margelo.nitro.audiobrowser.Section
|
|
6
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
6
7
|
import com.margelo.nitro.audiobrowser.Track
|
|
7
|
-
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
8
8
|
|
|
9
9
|
/** Builds MediaMetadata extras bundle for Android Auto/AAOS content styling. */
|
|
10
10
|
object MediaExtrasBuilder {
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Maps
|
|
14
|
-
* URI, uses CATEGORY_* variants which add margins around icons (better for
|
|
13
|
+
* Maps a display mode to the MediaConstants content style value. When artwork is an
|
|
14
|
+
* android.resource:// URI, uses CATEGORY_* variants which add margins around icons (better for
|
|
15
|
+
* small vector icons).
|
|
15
16
|
*
|
|
16
17
|
* @see <a href="https://developer.android.com/training/cars/media#default-content-style">Default
|
|
17
18
|
* content style</a>
|
|
18
19
|
*/
|
|
19
|
-
private fun
|
|
20
|
+
private fun StyleDisplay.toContentStyleValue(artwork: String?): Int {
|
|
20
21
|
val isAndroidResource = artwork?.startsWith("android.resource://") == true
|
|
21
22
|
return when (this) {
|
|
22
|
-
|
|
23
|
+
StyleDisplay.LIST ->
|
|
23
24
|
if (isAndroidResource) {
|
|
24
25
|
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
|
|
25
26
|
} else {
|
|
26
27
|
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
StyleDisplay.GRID ->
|
|
29
30
|
if (isAndroidResource) {
|
|
30
31
|
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
|
|
31
32
|
} else {
|
|
@@ -34,39 +35,43 @@ object MediaExtrasBuilder {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Extras for a track rendered on a browse surface. The owning [section] supplies the Android Auto
|
|
40
|
+
* group-title header hint (grouping is a per-item advisory in the flat MediaBrowser protocol —
|
|
41
|
+
* the section dies here, ADR 0010) and, from its page-folded style block, the per-item display
|
|
42
|
+
* hint: per-item hints derive from the section resolution, never from the track's own `display`,
|
|
43
|
+
* which is positional (ADR 0011).
|
|
44
|
+
*
|
|
45
|
+
* The track's own declared `display` is the page-layout *promise* for the page a browsable track
|
|
46
|
+
* opens — emitted as the parent-level BROWSABLE/PLAYABLE hint, the only below-root hint
|
|
47
|
+
* AOSP-derived AAOS media UIs honor. Declared or it doesn't exist: the library never derives a
|
|
48
|
+
* promise from pages it hasn't resolved, and never back-fills from ones it has.
|
|
49
|
+
*/
|
|
50
|
+
fun build(track: Track, section: Section? = null): Bundle =
|
|
38
51
|
build(
|
|
39
|
-
groupTitle =
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
groupTitle = section?.title,
|
|
53
|
+
itemDisplay = section?.style?.display,
|
|
54
|
+
promisedDisplay = if (track.src == null) track.style?.display else null,
|
|
42
55
|
artwork = track.artwork?.url,
|
|
43
56
|
)
|
|
44
57
|
|
|
45
|
-
fun build(resolvedTrack: ResolvedTrack): Bundle =
|
|
46
|
-
build(
|
|
47
|
-
groupTitle = resolvedTrack.groupTitle,
|
|
48
|
-
style = resolvedTrack.style,
|
|
49
|
-
childrenStyle = resolvedTrack.childrenStyle,
|
|
50
|
-
artwork = resolvedTrack.artwork?.url,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
58
|
private fun build(
|
|
54
59
|
groupTitle: String?,
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
itemDisplay: StyleDisplay?,
|
|
61
|
+
promisedDisplay: StyleDisplay?,
|
|
57
62
|
artwork: String?,
|
|
58
63
|
): Bundle =
|
|
59
64
|
Bundle().apply {
|
|
60
65
|
groupTitle?.let {
|
|
61
66
|
putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, it)
|
|
62
67
|
}
|
|
63
|
-
|
|
68
|
+
itemDisplay?.let {
|
|
64
69
|
putInt(
|
|
65
70
|
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
|
|
66
71
|
it.toContentStyleValue(artwork),
|
|
67
72
|
)
|
|
68
73
|
}
|
|
69
|
-
|
|
74
|
+
promisedDisplay?.let {
|
|
70
75
|
val styleValue = it.toContentStyleValue(artwork)
|
|
71
76
|
putInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, styleValue)
|
|
72
77
|
putInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, styleValue)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
package com.audiobrowser.util
|
|
2
|
+
|
|
3
|
+
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
4
|
+
import com.margelo.nitro.audiobrowser.CardImage
|
|
5
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
6
|
+
import com.margelo.nitro.audiobrowser.ImageShape
|
|
7
|
+
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
8
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
9
|
+
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The all-nil blocks a decode collapses to "no declaration". Every field is listed exactly once
|
|
13
|
+
* here — the decoders and the persistence restore compare against these instead of re-enumerating
|
|
14
|
+
* the fields in each guard (a constructor gaining a field breaks this file, not silently a guard).
|
|
15
|
+
*/
|
|
16
|
+
internal val EMPTY_TRACK_STYLE =
|
|
17
|
+
TrackStyle(
|
|
18
|
+
display = null,
|
|
19
|
+
artworkRendering = null,
|
|
20
|
+
imageShape = null,
|
|
21
|
+
accessorySymbol = null,
|
|
22
|
+
cardTint = null,
|
|
23
|
+
cardImage = null,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
internal val EMPTY_SECTION_STYLE =
|
|
27
|
+
SectionStyle(
|
|
28
|
+
display = null,
|
|
29
|
+
artworkRendering = null,
|
|
30
|
+
imageShape = null,
|
|
31
|
+
accessorySymbol = null,
|
|
32
|
+
cardTint = null,
|
|
33
|
+
cardImage = null,
|
|
34
|
+
gridWrap = null,
|
|
35
|
+
gridTile = null,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The style block's wire strings (ADR 0011) ↔ Nitro enums, shared by the browse decoder and the
|
|
40
|
+
* playback-state store. Inbound mapping is lenient: an unknown or empty value is "no declaration",
|
|
41
|
+
* never an error.
|
|
42
|
+
*/
|
|
43
|
+
internal fun StyleDisplay.toWireString(): String =
|
|
44
|
+
when (this) {
|
|
45
|
+
StyleDisplay.LIST -> "list"
|
|
46
|
+
StyleDisplay.GRID -> "grid"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
internal fun ArtworkRendering.toWireString(): String =
|
|
50
|
+
when (this) {
|
|
51
|
+
ArtworkRendering.ORIGINAL -> "original"
|
|
52
|
+
ArtworkRendering.STENCIL -> "stencil"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
internal fun String?.toStyleDisplay(): StyleDisplay? =
|
|
56
|
+
when (this?.takeIf { it.isNotEmpty() }?.lowercase()) {
|
|
57
|
+
"list" -> StyleDisplay.LIST
|
|
58
|
+
"grid" -> StyleDisplay.GRID
|
|
59
|
+
else -> null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
internal fun String?.toArtworkRendering(): ArtworkRendering? =
|
|
63
|
+
when (this?.takeIf { it.isNotEmpty() }?.lowercase()) {
|
|
64
|
+
"original" -> ArtworkRendering.ORIGINAL
|
|
65
|
+
"stencil" -> ArtworkRendering.STENCIL
|
|
66
|
+
else -> null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
internal fun ImageShape.toWireString(): String =
|
|
70
|
+
when (this) {
|
|
71
|
+
ImageShape.CIRCULAR -> "circular"
|
|
72
|
+
ImageShape.ROUNDED_RECTANGLE -> "rounded-rectangle"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
internal fun String?.toImageShape(): ImageShape? =
|
|
76
|
+
when (this?.takeIf { it.isNotEmpty() }?.lowercase()) {
|
|
77
|
+
"circular" -> ImageShape.CIRCULAR
|
|
78
|
+
"rounded-rectangle" -> ImageShape.ROUNDED_RECTANGLE
|
|
79
|
+
else -> null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
internal fun GridTile.toWireString(): String =
|
|
83
|
+
when (this) {
|
|
84
|
+
GridTile.PLAIN -> "plain"
|
|
85
|
+
GridTile.CARD -> "card"
|
|
86
|
+
GridTile.CONDENSED -> "condensed"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
internal fun String?.toGridTile(): GridTile? =
|
|
90
|
+
when (this?.takeIf { it.isNotEmpty() }?.lowercase()) {
|
|
91
|
+
"plain" -> GridTile.PLAIN
|
|
92
|
+
"card" -> GridTile.CARD
|
|
93
|
+
"condensed" -> GridTile.CONDENSED
|
|
94
|
+
else -> null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
internal fun CardImage.toWireString(): String =
|
|
98
|
+
when (this) {
|
|
99
|
+
CardImage.NORMAL -> "normal"
|
|
100
|
+
CardImage.BACKGROUND -> "background"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
internal fun String?.toCardImage(): CardImage? =
|
|
104
|
+
when (this?.takeIf { it.isNotEmpty() }?.lowercase()) {
|
|
105
|
+
"normal" -> CardImage.NORMAL
|
|
106
|
+
"background" -> CardImage.BACKGROUND
|
|
107
|
+
else -> null
|
|
108
|
+
}
|
|
@@ -7,17 +7,16 @@ import androidx.media3.common.MediaItem
|
|
|
7
7
|
import androidx.media3.common.MediaMetadata
|
|
8
8
|
import com.audiobrowser.browser.BrowseArtworkRegistry
|
|
9
9
|
import com.audiobrowser.browser.ResolvedArtwork
|
|
10
|
-
import com.margelo.nitro.audiobrowser.
|
|
10
|
+
import com.margelo.nitro.audiobrowser.Section
|
|
11
11
|
import com.margelo.nitro.audiobrowser.Track
|
|
12
12
|
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
13
|
-
import com.margelo.nitro.audiobrowser.Variant_String_ArtworkVariants
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* The single Track → Media3 [MediaItem] conversion. Owns the two easy-to-drift fallbacks: the
|
|
17
16
|
* displayed artwork is the transformed `artworkSource.uri` falling back to the raw `artwork` field,
|
|
18
|
-
* and the mediaId is the stable `id` for playable tracks, falling back to `
|
|
19
|
-
* must have one of the latter two — see the `src` vs `
|
|
20
|
-
* renders from `subtitle` (the now-playing line is re-stamped from `artist` by the now-playing
|
|
17
|
+
* and the mediaId is the stable `id` for playable tracks, falling back to `path` then `src` (a
|
|
18
|
+
* Track must have one of the latter two — see the `src` vs `path` note in CONTEXT.md). The list
|
|
19
|
+
* line renders from `subtitle` (the now-playing line is re-stamped from `artist` by the now-playing
|
|
21
20
|
* pipeline; see the Now Playing Metadata guide).
|
|
22
21
|
*/
|
|
23
22
|
object TrackFactory {
|
|
@@ -26,49 +25,42 @@ object TrackFactory {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* pass through unchanged.
|
|
28
|
+
* A synthetic browsable Track for a section's "view all" surface — the section has a path, title,
|
|
29
|
+
* and label, but no Track (ADR 0010). It carries the section's declared item block as its own,
|
|
30
|
+
* `display` included: the section's declaration about this content's layout is the only declared
|
|
31
|
+
* promise the "view all" page can ever have (there is no consumer-authored handle to declare one
|
|
32
|
+
* on), and without it Android Auto would always render that page as a list.
|
|
35
33
|
*/
|
|
36
|
-
fun
|
|
37
|
-
tracks.flatMap { track ->
|
|
38
|
-
val items = track.imageRow
|
|
39
|
-
if (items.isNullOrEmpty()) return@flatMap listOf(track)
|
|
40
|
-
val expanded = items.map { it.toTrack(groupTitle = track.title) }
|
|
41
|
-
if (track.url != null) {
|
|
42
|
-
expanded + track.copy(imageRow = null, groupTitle = track.title)
|
|
43
|
-
} else {
|
|
44
|
-
expanded
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The row-item equivalent of a full Track, for surfaces that render items as plain rows. */
|
|
49
|
-
fun ImageRowItem.toTrack(groupTitle: String?): Track =
|
|
34
|
+
fun navigationTrack(section: Section): Track =
|
|
50
35
|
Track(
|
|
51
|
-
id =
|
|
52
|
-
|
|
53
|
-
src =
|
|
54
|
-
artwork =
|
|
55
|
-
artworkSource =
|
|
56
|
-
request =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
album = album,
|
|
36
|
+
id = null,
|
|
37
|
+
path = section.path,
|
|
38
|
+
src = null,
|
|
39
|
+
artwork = null,
|
|
40
|
+
artworkSource = null,
|
|
41
|
+
request = null,
|
|
42
|
+
title = section.title ?: "",
|
|
43
|
+
subtitle = section.subtitle,
|
|
44
|
+
artist = null,
|
|
45
|
+
albumPath = null,
|
|
46
|
+
album = null,
|
|
63
47
|
description = null,
|
|
64
48
|
genre = null,
|
|
65
49
|
duration = null,
|
|
66
|
-
style =
|
|
67
|
-
|
|
50
|
+
style =
|
|
51
|
+
section.style?.let {
|
|
52
|
+
TrackStyle(
|
|
53
|
+
display = it.display,
|
|
54
|
+
artworkRendering = it.artworkRendering,
|
|
55
|
+
imageShape = it.imageShape,
|
|
56
|
+
accessorySymbol = it.accessorySymbol,
|
|
57
|
+
cardTint = it.cardTint,
|
|
58
|
+
cardImage = it.cardImage,
|
|
59
|
+
)
|
|
60
|
+
},
|
|
61
|
+
disabled = null,
|
|
68
62
|
favorited = null,
|
|
69
|
-
|
|
70
|
-
live = live,
|
|
71
|
-
imageRow = null,
|
|
63
|
+
live = null,
|
|
72
64
|
)
|
|
73
65
|
|
|
74
66
|
fun toMedia3(tracks: Array<Track>): List<MediaItem> {
|
|
@@ -90,10 +82,11 @@ object TrackFactory {
|
|
|
90
82
|
track: Track,
|
|
91
83
|
registry: BrowseArtworkRegistry,
|
|
92
84
|
authority: String,
|
|
85
|
+
section: Section? = null,
|
|
93
86
|
): MediaItem {
|
|
94
87
|
val rawUrl = artworkUri(track) // artworkSource.uri ?: artwork
|
|
95
88
|
val scheme = rawUrl?.let { Uri.parse(it).scheme?.lowercase() }
|
|
96
|
-
val builder = metadataBuilder(track)
|
|
89
|
+
val builder = metadataBuilder(track, section)
|
|
97
90
|
if (rawUrl != null && (scheme == "http" || scheme == "https")) {
|
|
98
91
|
val isSvg =
|
|
99
92
|
SvgArtworkRenderer.isSvgUrl(rawUrl) || SvgArtworkRenderer.isSvgUrl(track.artwork?.url)
|
|
@@ -109,8 +102,11 @@ object TrackFactory {
|
|
|
109
102
|
/** The transformed artworkSource wins over the raw artwork field. */
|
|
110
103
|
private fun artworkUri(track: Track): String? = track.artworkSource?.uri ?: track.artwork?.url
|
|
111
104
|
|
|
112
|
-
/**
|
|
113
|
-
|
|
105
|
+
/**
|
|
106
|
+
* All metadata except artwork. The owning [section] (browse surfaces only) supplies the
|
|
107
|
+
* group-title header hint and the per-item style default (ADR 0010).
|
|
108
|
+
*/
|
|
109
|
+
private fun metadataBuilder(track: Track, section: Section? = null): MediaMetadata.Builder =
|
|
114
110
|
MediaMetadata.Builder()
|
|
115
111
|
.setTitle(track.title)
|
|
116
112
|
.setArtist(track.subtitle)
|
|
@@ -119,26 +115,29 @@ object TrackFactory {
|
|
|
119
115
|
.setGenre(track.genre)
|
|
120
116
|
.setIsBrowsable(track.src == null)
|
|
121
117
|
.setIsPlayable(track.src != null)
|
|
122
|
-
.setExtras(MediaExtrasBuilder.build(track))
|
|
118
|
+
.setExtras(MediaExtrasBuilder.build(track, section))
|
|
123
119
|
.apply { track.favorited?.let { setUserRating(HeartRating(it)) } }
|
|
124
120
|
|
|
125
121
|
private fun buildMediaItem(track: Track, metadata: MediaMetadata): MediaItem {
|
|
126
|
-
// A playable track's mediaId is the consumer's stable `id` when it has one
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// `
|
|
122
|
+
// A playable track's mediaId is the consumer's stable `id` when it has one —
|
|
123
|
+
// the identity rule (id when non-blank, else src; see Track.identity /
|
|
124
|
+
// ADR 0008), except that an id-less playable track prefers its contextual
|
|
125
|
+
// `path` over `src` here, so this is deliberately NOT expressed via
|
|
126
|
+
// Track.identity. Android Auto marks the "now playing" browse row by exact
|
|
127
|
+
// mediaId equality between the row and the player's current item (media3
|
|
128
|
+
// announces `currentMediaItem.mediaId` in the legacy playback state), and a
|
|
130
129
|
// consumer-loaded track's `src` can differ textually from the browse row's
|
|
131
130
|
// for the same item (absolute vs relative, extra query params) — the id is
|
|
132
131
|
// the one string both sides share. It is also context-free, so the same
|
|
133
132
|
// station gets the same mediaId in every tab. Browsable-only tracks keep
|
|
134
|
-
// `
|
|
133
|
+
// `path` so navigation parentIds stay resolvable paths.
|
|
135
134
|
val stableId = if (track.src != null) track.id?.takeUnless { it.isBlank() } else null
|
|
136
135
|
val mediaId =
|
|
137
136
|
stableId
|
|
138
|
-
?: track.
|
|
137
|
+
?: track.path
|
|
139
138
|
?: track.src
|
|
140
139
|
?: throw IllegalArgumentException(
|
|
141
|
-
"Track must have either
|
|
140
|
+
"Track must have either path or src defined. Track: title='${track.title}', artist='${track.artist}'"
|
|
142
141
|
)
|
|
143
142
|
return MediaItem.Builder()
|
|
144
143
|
.setMediaId(mediaId)
|