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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
package com.audiobrowser.browser
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.TestFixtures.sectionStyle
|
|
4
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
5
|
+
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
6
|
+
import com.margelo.nitro.audiobrowser.CardImage
|
|
7
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
8
|
+
import com.margelo.nitro.audiobrowser.ImageShape
|
|
9
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
10
|
+
import org.junit.Assert.assertEquals
|
|
11
|
+
import org.junit.Assert.assertNull
|
|
12
|
+
import org.junit.Test
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolution semantics (ADR 0011): container properties and `display` resolve by scope override
|
|
16
|
+
* (`section ?? page`), inherited item properties resolve `track ?? section ?? page` — and `display`
|
|
17
|
+
* never inherits onto an item.
|
|
18
|
+
*/
|
|
19
|
+
class StyleResolverTest {
|
|
20
|
+
|
|
21
|
+
@Test
|
|
22
|
+
fun `section overrides the page per property`() {
|
|
23
|
+
val resolved =
|
|
24
|
+
StyleResolver.sectionStyle(
|
|
25
|
+
section =
|
|
26
|
+
sectionStyle(display = StyleDisplay.LIST, artworkRendering = null, gridWrap = null),
|
|
27
|
+
page =
|
|
28
|
+
sectionStyle(
|
|
29
|
+
display = StyleDisplay.GRID,
|
|
30
|
+
artworkRendering = ArtworkRendering.STENCIL,
|
|
31
|
+
gridWrap = false,
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
// Declared on the section: wins. Undeclared: the page's scope-wide value.
|
|
35
|
+
assertEquals(StyleDisplay.LIST, resolved.display)
|
|
36
|
+
assertEquals(ArtworkRendering.STENCIL, resolved.artworkRendering)
|
|
37
|
+
assertEquals(false, resolved.gridWrap)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Test
|
|
41
|
+
fun `a styleless section takes the page block`() {
|
|
42
|
+
val resolved =
|
|
43
|
+
StyleResolver.sectionStyle(
|
|
44
|
+
section = null,
|
|
45
|
+
page = sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = true),
|
|
46
|
+
)
|
|
47
|
+
assertEquals(StyleDisplay.GRID, resolved.display)
|
|
48
|
+
assertEquals(true, resolved.gridWrap)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `track overrides the section per property`() {
|
|
53
|
+
val resolved =
|
|
54
|
+
StyleResolver.trackStyle(
|
|
55
|
+
track = trackStyle(display = null, artworkRendering = ArtworkRendering.ORIGINAL),
|
|
56
|
+
section =
|
|
57
|
+
sectionStyle(
|
|
58
|
+
display = StyleDisplay.GRID,
|
|
59
|
+
artworkRendering = ArtworkRendering.STENCIL,
|
|
60
|
+
gridWrap = null,
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
assertEquals(ArtworkRendering.ORIGINAL, resolved.artworkRendering)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
fun `a styleless track inherits the section value`() {
|
|
68
|
+
val resolved =
|
|
69
|
+
StyleResolver.trackStyle(
|
|
70
|
+
track = null,
|
|
71
|
+
section =
|
|
72
|
+
sectionStyle(display = null, artworkRendering = ArtworkRendering.STENCIL, gridWrap = null),
|
|
73
|
+
)
|
|
74
|
+
assertEquals(ArtworkRendering.STENCIL, resolved.artworkRendering)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Test
|
|
78
|
+
fun `display never inherits onto an item`() {
|
|
79
|
+
// The track's own display is the page promise for the page IT opens; the
|
|
80
|
+
// section's describes its children's layout. Neither may surface as the
|
|
81
|
+
// item's resolved display.
|
|
82
|
+
val resolved =
|
|
83
|
+
StyleResolver.trackStyle(
|
|
84
|
+
track = trackStyle(display = StyleDisplay.GRID, artworkRendering = null),
|
|
85
|
+
section =
|
|
86
|
+
sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = null),
|
|
87
|
+
)
|
|
88
|
+
assertNull(resolved.display)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun `imageShape inherits and overrides per item`() {
|
|
93
|
+
// Section-wide circular (an artists shelf); one album overrides.
|
|
94
|
+
val inherited =
|
|
95
|
+
StyleResolver.trackStyle(
|
|
96
|
+
track = null,
|
|
97
|
+
section = sectionStyle(imageShape = ImageShape.CIRCULAR),
|
|
98
|
+
)
|
|
99
|
+
assertEquals(ImageShape.CIRCULAR, inherited.imageShape)
|
|
100
|
+
val overridden =
|
|
101
|
+
StyleResolver.trackStyle(
|
|
102
|
+
track = trackStyle(imageShape = ImageShape.ROUNDED_RECTANGLE),
|
|
103
|
+
section = sectionStyle(imageShape = ImageShape.CIRCULAR),
|
|
104
|
+
)
|
|
105
|
+
assertEquals(ImageShape.ROUNDED_RECTANGLE, overridden.imageShape)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun `accessorySymbol inherits and 'none' resolves as a value`() {
|
|
110
|
+
val inherited =
|
|
111
|
+
StyleResolver.trackStyle(track = null, section = sectionStyle(accessorySymbol = "lock.fill"))
|
|
112
|
+
assertEquals("lock.fill", inherited.accessorySymbol)
|
|
113
|
+
// 'none' is the inheritance escape — it must survive resolution intact
|
|
114
|
+
// (the renderer, not the resolver, maps it to "no accessory").
|
|
115
|
+
val escaped =
|
|
116
|
+
StyleResolver.trackStyle(
|
|
117
|
+
track = trackStyle(accessorySymbol = "none"),
|
|
118
|
+
section = sectionStyle(accessorySymbol = "lock.fill"),
|
|
119
|
+
)
|
|
120
|
+
assertEquals("none", escaped.accessorySymbol)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Test
|
|
124
|
+
fun `gridTile resolves by scope override`() {
|
|
125
|
+
// A page-wide card treatment; one section opts back to plain tiles.
|
|
126
|
+
val inherited =
|
|
127
|
+
StyleResolver.sectionStyle(section = null, page = sectionStyle(gridTile = GridTile.CARD))
|
|
128
|
+
assertEquals(GridTile.CARD, inherited.gridTile)
|
|
129
|
+
val overridden =
|
|
130
|
+
StyleResolver.sectionStyle(
|
|
131
|
+
section = sectionStyle(gridTile = GridTile.PLAIN),
|
|
132
|
+
page = sectionStyle(gridTile = GridTile.CARD),
|
|
133
|
+
)
|
|
134
|
+
assertEquals(GridTile.PLAIN, overridden.gridTile)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `card properties inherit and override per item`() {
|
|
139
|
+
val inherited =
|
|
140
|
+
StyleResolver.trackStyle(
|
|
141
|
+
track = null,
|
|
142
|
+
section = sectionStyle(cardTint = "#1e3a8a", cardImage = CardImage.BACKGROUND),
|
|
143
|
+
)
|
|
144
|
+
assertEquals("#1e3a8a", inherited.cardTint)
|
|
145
|
+
assertEquals(CardImage.BACKGROUND, inherited.cardImage)
|
|
146
|
+
val overridden =
|
|
147
|
+
StyleResolver.trackStyle(
|
|
148
|
+
track = trackStyle(cardImage = CardImage.NORMAL),
|
|
149
|
+
section = sectionStyle(cardTint = "#1e3a8a", cardImage = CardImage.BACKGROUND),
|
|
150
|
+
)
|
|
151
|
+
assertEquals(CardImage.NORMAL, overridden.cardImage)
|
|
152
|
+
assertEquals("#1e3a8a", overridden.cardTint)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.audiobrowser.extension
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.TestFixtures.track
|
|
4
|
+
import com.audiobrowser.util.BrowserPathHelper
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
import org.junit.runner.RunWith
|
|
8
|
+
import org.robolectric.RobolectricTestRunner
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The skip-in-place queue match (ADR 0009): exact contextual-path equality pins the tapped copy;
|
|
12
|
+
* the identity match remains for index-less paths; an index-stamped path with no exact match
|
|
13
|
+
* misses, so the caller re-expands. Robolectric for `android.net.Uri` (contextual-path parsing).
|
|
14
|
+
*/
|
|
15
|
+
@RunWith(RobolectricTestRunner::class)
|
|
16
|
+
class TappedTrackTest {
|
|
17
|
+
|
|
18
|
+
private fun stamped(id: String, index: Int) = BrowserPathHelper.build("/page", id, index)
|
|
19
|
+
|
|
20
|
+
private val queue =
|
|
21
|
+
arrayOf(
|
|
22
|
+
track(src = "a").copy(path = stamped("a", 0)),
|
|
23
|
+
track(src = "b").copy(path = stamped("b", 1)),
|
|
24
|
+
track(src = "a").copy(path = stamped("a", 2)),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@Test
|
|
28
|
+
fun `exact path match pins the tapped copy of a duplicate identity`() {
|
|
29
|
+
assertEquals(2, queue.indexOfTappedTrack(stamped("a", 2), "a"))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Test
|
|
33
|
+
fun `index-less path falls back to the identity match`() {
|
|
34
|
+
assertEquals(0, queue.indexOfTappedTrack(BrowserPathHelper.build("/page", "a"), "a"))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test
|
|
38
|
+
fun `index-stamped path with no exact match misses instead of identity-skipping`() {
|
|
39
|
+
// A tap in another section whose identity also sits in this queue: the
|
|
40
|
+
// caller must re-expand (re-scoping to the tapped section), not skip.
|
|
41
|
+
assertEquals(-1, queue.indexOfTappedTrack(stamped("a", 7), "a"))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package com.audiobrowser.extension
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.TestFixtures.track
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertNull
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* THE identity rule (ADR 0008): a track's identity is its `id` when non-blank, else its `src`.
|
|
10
|
+
* Everything that compares tracks — favorites, section scoping, skip-in-place, `__trackId` — goes
|
|
11
|
+
* through this extension.
|
|
12
|
+
*/
|
|
13
|
+
class TrackIdentityTest {
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun `id wins over src`() {
|
|
17
|
+
assertEquals("stable-id", track(id = "stable-id", src = "https://s/a.mp3").identity)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Test
|
|
21
|
+
fun `blank id falls back to src`() {
|
|
22
|
+
assertEquals("https://s/a.mp3", track(id = "", src = "https://s/a.mp3").identity)
|
|
23
|
+
assertEquals("https://s/a.mp3", track(id = " ", src = "https://s/a.mp3").identity)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Test
|
|
27
|
+
fun `missing id falls back to src`() {
|
|
28
|
+
assertEquals("https://s/a.mp3", track(id = null, src = "https://s/a.mp3").identity)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
fun `id alone is an identity`() {
|
|
33
|
+
assertEquals("stable-id", track(id = "stable-id", src = null).identity)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
fun `neither id nor src means no identity`() {
|
|
38
|
+
assertNull(track(id = null, src = null).identity)
|
|
39
|
+
assertNull(track(id = "", src = null).identity)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -3,7 +3,6 @@ package com.audiobrowser.player
|
|
|
3
3
|
import com.margelo.nitro.audiobrowser.PlayerCapabilities
|
|
4
4
|
import com.margelo.nitro.audiobrowser.RemoteButton
|
|
5
5
|
import com.margelo.nitro.audiobrowser.RemoteButtonLayout
|
|
6
|
-
import com.margelo.nitro.audiobrowser.Variant_Boolean_FavoriteConfig
|
|
7
6
|
import org.junit.Assert.assertEquals
|
|
8
7
|
import org.junit.Assert.assertFalse
|
|
9
8
|
import org.junit.Assert.assertTrue
|
|
@@ -36,7 +35,7 @@ class CapabilityControlsTest {
|
|
|
36
35
|
skipToPrevious = skipToPrevious,
|
|
37
36
|
jumpForward = jumpForward,
|
|
38
37
|
jumpBackward = jumpBackward,
|
|
39
|
-
favorite = favorite
|
|
38
|
+
favorite = favorite,
|
|
40
39
|
shuffleMode = null,
|
|
41
40
|
repeatMode = null,
|
|
42
41
|
playbackRate = null,
|
|
@@ -377,7 +377,7 @@ class NowPlayingUpdaterTest {
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
@Test
|
|
380
|
-
fun `artwork re-resolves for a new track
|
|
380
|
+
fun `artwork re-resolves for a new track identity including id-less tracks`() = runTest {
|
|
381
381
|
val surface =
|
|
382
382
|
FakeSurface().apply {
|
|
383
383
|
currentTrack = track("A", id = "a")
|
|
@@ -393,10 +393,31 @@ class NowPlayingUpdaterTest {
|
|
|
393
393
|
runCurrent()
|
|
394
394
|
assertEquals(2, surface.artworkResolveCount)
|
|
395
395
|
|
|
396
|
+
// The config runs for id-less tracks too — no blanket skip.
|
|
396
397
|
surface.currentTrack = track("C", id = null, src = "https://s/c.mp3")
|
|
397
398
|
updater.render()
|
|
398
399
|
runCurrent()
|
|
399
|
-
assertEquals(
|
|
400
|
+
assertEquals(3, surface.artworkResolveCount)
|
|
401
|
+
assertEquals(0 to "https://img/a.png", surface.artworkStamps.last())
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
@Test
|
|
405
|
+
fun `artwork for an id-less track resolves once keyed on src identity`() = runTest {
|
|
406
|
+
val surface =
|
|
407
|
+
FakeSurface().apply {
|
|
408
|
+
currentTrack = track("C", id = null, src = "https://s/c.mp3")
|
|
409
|
+
hasNowPlayingArtworkConfig = true
|
|
410
|
+
artworkResult = "https://img/c.png"
|
|
411
|
+
}
|
|
412
|
+
val updater = NowPlayingUpdater(surface, backgroundScope)
|
|
413
|
+
|
|
414
|
+
updater.render()
|
|
415
|
+
runCurrent()
|
|
416
|
+
updater.updateNowPlaying(update(title = "Song")) // re-render, same track
|
|
417
|
+
runCurrent()
|
|
418
|
+
|
|
419
|
+
assertEquals(1, surface.artworkResolveCount)
|
|
420
|
+
assertEquals(listOf(0 to "https://img/c.png"), surface.artworkStamps)
|
|
400
421
|
}
|
|
401
422
|
|
|
402
423
|
@Test
|
|
@@ -439,4 +460,23 @@ class NowPlayingUpdaterTest {
|
|
|
439
460
|
|
|
440
461
|
assertTrue(surface.artworkStamps.isEmpty())
|
|
441
462
|
}
|
|
463
|
+
|
|
464
|
+
@Test
|
|
465
|
+
fun `a stale artwork result for an id-less track is not stamped after a track change`() =
|
|
466
|
+
runTest {
|
|
467
|
+
val surface =
|
|
468
|
+
FakeSurface().apply {
|
|
469
|
+
currentTrack = track("A", id = null, src = "https://s/a.mp3")
|
|
470
|
+
hasNowPlayingArtworkConfig = true
|
|
471
|
+
artworkResult = "https://img/a.png"
|
|
472
|
+
}
|
|
473
|
+
val updater = NowPlayingUpdater(surface, backgroundScope)
|
|
474
|
+
|
|
475
|
+
updater.render() // schedules the artwork resolve
|
|
476
|
+
// Skip to another id-less track before it lands: identity (src) keying must catch it.
|
|
477
|
+
surface.currentTrack = track("B", id = null, src = "https://s/b.mp3")
|
|
478
|
+
runCurrent()
|
|
479
|
+
|
|
480
|
+
assertTrue(surface.artworkStamps.isEmpty())
|
|
481
|
+
}
|
|
442
482
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.TestFixtures
|
|
4
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
5
|
+
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
6
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
7
|
+
import org.junit.Assert.assertEquals
|
|
8
|
+
import org.junit.Assert.assertNull
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
import org.junit.runner.RunWith
|
|
11
|
+
import org.robolectric.RobolectricTestRunner
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bespoke org.json persistence of the resumption track. ADR 0011: the style block and
|
|
15
|
+
* `disabled` survive process death (a restored queue keeps its visible fidelity), and a snapshot
|
|
16
|
+
* written before the block model — `style` as an enum-name string — restores without a declaration
|
|
17
|
+
* rather than failing.
|
|
18
|
+
*/
|
|
19
|
+
@RunWith(RobolectricTestRunner::class)
|
|
20
|
+
class PlaybackStateStoreTrackJsonTest {
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
fun `style and disabled survive the round-trip`() {
|
|
24
|
+
val track =
|
|
25
|
+
TestFixtures.track(title = "Night Mix", src = "https://s/a.mp3", disabled = true)
|
|
26
|
+
.copy(
|
|
27
|
+
style =
|
|
28
|
+
trackStyle(display = StyleDisplay.GRID, artworkRendering = ArtworkRendering.STENCIL)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
val restored = PlaybackStateStore.trackFromJson(PlaybackStateStore.trackToJson(track))
|
|
32
|
+
|
|
33
|
+
assertEquals(StyleDisplay.GRID, restored?.style?.display)
|
|
34
|
+
assertEquals(ArtworkRendering.STENCIL, restored?.style?.artworkRendering)
|
|
35
|
+
assertEquals(true, restored?.disabled)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
fun `a track without style round-trips without one`() {
|
|
40
|
+
val track = TestFixtures.track(title = "Plain", src = "https://s/a.mp3")
|
|
41
|
+
|
|
42
|
+
val restored = PlaybackStateStore.trackFromJson(PlaybackStateStore.trackToJson(track))
|
|
43
|
+
|
|
44
|
+
assertNull(restored?.style)
|
|
45
|
+
assertNull(restored?.disabled)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Test
|
|
49
|
+
fun `a pre-block snapshot with a string style restores without a declaration`() {
|
|
50
|
+
val restored = PlaybackStateStore.trackFromJson("""{"title":"X","src":"s","style":"LIST"}""")
|
|
51
|
+
|
|
52
|
+
assertEquals("X", restored?.title)
|
|
53
|
+
assertNull(restored?.style)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.C
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertNotEquals
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Media3's `ShuffleOrder.cloneAndSet(insertionCount, startIndex)` discards the start index (the
|
|
11
|
+
* interface default clears and re-inserts from 0, and `DefaultShuffleOrder` does not override it),
|
|
12
|
+
* so `setMediaItems(items, startIndex, …)` leaves the starting track at a random shuffle position —
|
|
13
|
+
* last, for a 1-in-N share of queues, which reads as end-of-queue from the very first track.
|
|
14
|
+
*/
|
|
15
|
+
class ShuffleOrderLedByTest {
|
|
16
|
+
@Test
|
|
17
|
+
fun `the start index leads the order`() {
|
|
18
|
+
for (start in 0 until 6) {
|
|
19
|
+
val order = shuffleOrderLedBy(startIndex = start, count = 6)
|
|
20
|
+
assertEquals("start index must be at shuffle position 0", start, order.firstIndex)
|
|
21
|
+
assertNotEquals(
|
|
22
|
+
"a queue of 6 must have somewhere to advance to from its start track",
|
|
23
|
+
C.INDEX_UNSET.toLong(),
|
|
24
|
+
order.getNextIndex(start).toLong(),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun `every index appears exactly once`() {
|
|
31
|
+
val order = shuffleOrderLedBy(startIndex = 3, count = 8)
|
|
32
|
+
assertEquals(8, order.length)
|
|
33
|
+
val seen = mutableListOf<Int>()
|
|
34
|
+
var index = order.firstIndex
|
|
35
|
+
while (index != C.INDEX_UNSET) {
|
|
36
|
+
seen.add(index)
|
|
37
|
+
index = order.getNextIndex(index)
|
|
38
|
+
}
|
|
39
|
+
assertEquals((0 until 8).toList(), seen.sorted())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun `a single-track queue leads with its only track and ends there`() {
|
|
44
|
+
val order = shuffleOrderLedBy(startIndex = 0, count = 1)
|
|
45
|
+
assertEquals(0, order.firstIndex)
|
|
46
|
+
assertEquals(C.INDEX_UNSET, order.getNextIndex(0))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun `an out-of-range start index is clamped rather than throwing`() {
|
|
51
|
+
assertEquals(4, shuffleOrderLedBy(startIndex = 99, count = 5).firstIndex)
|
|
52
|
+
assertEquals(0, shuffleOrderLedBy(startIndex = -3, count = 5).firstIndex)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Test
|
|
56
|
+
fun `an empty queue produces an empty order`() {
|
|
57
|
+
assertEquals(0, shuffleOrderLedBy(startIndex = 0, count = 0).length)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Test
|
|
61
|
+
fun `the tail is actually shuffled`() {
|
|
62
|
+
// Not a strict guarantee of any single draw — but across many draws an
|
|
63
|
+
// unshuffled tail would be impossible to miss.
|
|
64
|
+
val tails =
|
|
65
|
+
(0 until 50)
|
|
66
|
+
.map { shuffleOrderLedBy(startIndex = 0, count = 8) }
|
|
67
|
+
.map { order ->
|
|
68
|
+
buildList {
|
|
69
|
+
var index = order.getNextIndex(order.firstIndex)
|
|
70
|
+
while (index != C.INDEX_UNSET) {
|
|
71
|
+
add(index)
|
|
72
|
+
index = order.getNextIndex(index)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
.toSet()
|
|
77
|
+
assertTrue("50 draws produced one identical tail — the order is not shuffled", tails.size > 1)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,28 +1,53 @@
|
|
|
1
1
|
package com.audiobrowser.util
|
|
2
2
|
|
|
3
|
-
import org.junit.Assert.
|
|
4
|
-
import org.junit.Assert.
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertNull
|
|
5
5
|
import org.junit.Test
|
|
6
|
+
import org.junit.runner.RunWith
|
|
7
|
+
import org.robolectric.RobolectricTestRunner
|
|
6
8
|
|
|
9
|
+
@RunWith(RobolectricTestRunner::class)
|
|
7
10
|
class BrowserPathHelperTest {
|
|
8
11
|
|
|
9
12
|
@Test
|
|
10
|
-
fun `
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
fun `build round-trips a src carrying its own query params`() {
|
|
14
|
+
// A src carrying its own query string (signed CDN URL) must survive the
|
|
15
|
+
// build → extract/strip round-trip: an unescaped `&` would split the src
|
|
16
|
+
// into stray query params, truncating the trackId and polluting the
|
|
17
|
+
// parent path.
|
|
18
|
+
val src = "https://cdn.example.com/stream.mp3?token=abc&exp=1699999999"
|
|
19
|
+
val url = BrowserPathHelper.build("/library", src)
|
|
20
|
+
assertEquals(src, BrowserPathHelper.extractTrackId(url))
|
|
21
|
+
assertEquals("/library", BrowserPathHelper.stripTrackId(url))
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
@Test
|
|
20
|
-
fun `
|
|
21
|
-
val
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
fun `build round-trips a src with equals and plus`() {
|
|
26
|
+
val src = "https://cdn.example.com/a+b.mp3?sig=x=y"
|
|
27
|
+
val url = BrowserPathHelper.build("/library", src)
|
|
28
|
+
assertEquals(src, BrowserPathHelper.extractTrackId(url))
|
|
29
|
+
assertEquals("/library", BrowserPathHelper.stripTrackId(url))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Test
|
|
33
|
+
fun `build stamps the index and round-trips it`() {
|
|
34
|
+
val src = "https://cdn.example.com/stream.mp3?token=abc&exp=1"
|
|
35
|
+
val url = BrowserPathHelper.build("/library", src, 3)
|
|
36
|
+
assertEquals(src, BrowserPathHelper.extractTrackId(url))
|
|
37
|
+
assertEquals(3, BrowserPathHelper.extractIndex(url))
|
|
38
|
+
assertEquals("/library", BrowserPathHelper.stripTrackId(url))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `extractIndex returns null without a stamped index`() {
|
|
43
|
+
val url = BrowserPathHelper.build("/library", "song.mp3")
|
|
44
|
+
assertNull(BrowserPathHelper.extractIndex(url))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Test
|
|
48
|
+
fun `extractIndex returns null for non-contextual or malformed-index URLs`() {
|
|
49
|
+
assertNull(BrowserPathHelper.extractIndex("/library"))
|
|
50
|
+
assertNull(BrowserPathHelper.extractIndex("/library?__trackId=song.mp3&__index=x"))
|
|
51
|
+
assertNull(BrowserPathHelper.extractIndex("/library?__trackId=song.mp3&__index=-1"))
|
|
27
52
|
}
|
|
28
53
|
}
|