react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.21
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 +35 -21
- package/android/build.gradle +35 -12
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
- package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -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/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
- package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
- package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -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 +56 -52
- package/android/src/main/res/values/strings.xml +25 -5
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
- 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 +3 -2
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
- 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/model/IcyTitleTest.kt +49 -0
- package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
- package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -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 +230 -205
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +264 -106
- package/ios/Browser/SearchDrillIn.swift +7 -7
- 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 +42 -20
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
- package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
- package/ios/Extension/Section+Copying.swift +29 -0
- package/ios/Extension/String+IcyCharset.swift +21 -0
- package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
- package/ios/Extension/Track+Copying.swift +8 -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 +253 -100
- package/ios/Model/NitroTypeStubs.swift +125 -23
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/Option/SessionCategories.swift +3 -2
- package/ios/Player/MediaIntentCriteria.swift +17 -9
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackErrorHandler.swift +4 -4
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +46 -13
- package/ios/Player/RetryManager.swift +80 -18
- package/ios/Player/SeekCompletionHandler.swift +3 -1
- package/ios/Player/ShuffleOrder.swift +33 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
- 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/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
- package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/QueueManagerTests.swift +122 -10
- package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
- package/ios/Tests/RetryManagerTests.swift +89 -0
- 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 +8 -1
- package/ios/Tests/TabBarEntriesTests.swift +19 -19
- package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
- 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 +63 -35
- package/ios/TrackPlayerCallbacks.swift +5 -38
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/LRUCache.swift +4 -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 +8 -8
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +12 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +11 -3
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +1 -1
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +3 -3
- package/lib/commonjs/features/favorites.js +14 -11
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/gate.js +16 -1
- package/lib/commonjs/features/gate.js.map +1 -1
- package/lib/commonjs/features/metadata.js +20 -3
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +1 -1
- package/lib/commonjs/features/output.js +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/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +3 -2
- 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 +3 -4
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +48 -34
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +18 -9
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +23 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +15 -4
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +13 -6
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +13 -92
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +13 -1
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
- package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
- package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
- 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 +200 -75
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
- package/lib/commonjs/web/TrackPlayer/Player.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/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +175 -100
- 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/SearchManager.js +18 -23
- package/lib/commonjs/web/browser/SearchManager.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/http/RequestConfigBuilder.js +110 -27
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
- package/lib/commonjs/web/http/followMediaRedirect.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 +2 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +12 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +11 -3
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +1 -1
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +3 -3
- package/lib/module/features/favorites.js +14 -11
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/gate.js +16 -1
- package/lib/module/features/gate.js.map +1 -1
- package/lib/module/features/metadata.js +20 -3
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +1 -1
- package/lib/module/features/output.js +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/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +3 -2
- 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 +3 -4
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +0 -5
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +47 -32
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +17 -8
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +19 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +15 -4
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +13 -6
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -92
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +13 -1
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/test-utils/reactNativeStub.js +58 -0
- package/lib/module/test-utils/reactNativeStub.js.map +1 -0
- package/lib/module/utils/LazyNativeEmitter.js +28 -14
- 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 +200 -75
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
- package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +254 -39
- package/lib/module/web/TrackPlayer/Player.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/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +175 -100
- 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/SearchManager.js +18 -23
- package/lib/module/web/browser/SearchManager.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/http/RequestConfigBuilder.js +110 -27
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/http/followMediaRedirect.js +215 -0
- package/lib/module/web/http/followMediaRedirect.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/package.json +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +1 -1
- 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 +10 -2
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +1 -1
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +3 -3
- package/lib/typescript/src/features/favorites.d.ts +14 -11
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/gate.d.ts +18 -2
- package/lib/typescript/src/features/gate.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +20 -3
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/output.d.ts +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/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +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 +3 -4
- 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/playback/volume.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts +108 -56
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +14 -4
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +7 -4
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -22
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
- package/lib/typescript/src/types/browser-native.d.ts +3 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +82 -57
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
- 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 +1 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
- 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__string__.hpp +102 -0
- 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 +7 -10
- 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 +45 -25
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
- 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 +36 -11
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
- package/nitrogen/generated/android/c++/JSection.hpp +116 -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 +25 -46
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
- 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/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
- 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 +8 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
- 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/{NotificationButton.kt → RemoteButton.kt} +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -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 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
- 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/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
- 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 +52 -5
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
- package/nitrogen/generated/ios/swift/Section.swift +82 -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 +28 -63
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
- 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++/HybridAudioBrowserSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
- 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 +10 -6
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
- package/nitrogen/generated/shared/c++/Section.hpp +102 -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 +18 -30
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +24 -11
- package/src/AudioBrowser.ts +2 -1
- package/src/CLAUDE.md +52 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +12 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +11 -3
- package/src/features/carConnection.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +3 -3
- package/src/features/favorites.ts +14 -11
- package/src/features/gate.ts +16 -1
- package/src/features/hook-wiring.test.ts +302 -0
- package/src/features/metadata.ts +20 -3
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.test.ts +169 -0
- package/src/features/playback/progress.ts +3 -2
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +3 -4
- package/src/features/playback/trackPlaybackTime.test.ts +16 -6
- package/src/features/playback/trackPlaybackTime.ts +1 -6
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.ts +126 -76
- package/src/features/player/setup.test.ts +24 -7
- package/src/features/player/setup.ts +22 -10
- package/src/features/player/validateOptions.ts +21 -0
- package/src/features/queue/activeTrack.ts +15 -4
- package/src/features/queue/queue.ts +13 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -92
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +9 -1
- package/src/nitro-stub-drift.test.ts +277 -0
- package/src/public-surface.test.ts +147 -0
- package/src/specs/audio-browser.nitro.ts +5 -4
- package/src/style-resolution.test.ts +210 -0
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +7 -3
- package/src/types/browser-nodes.ts +396 -134
- package/src/types/browser.ts +85 -59
- package/src/utils/LazyNativeEmitter.ts +28 -15
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
- package/src/web/NativeAudioBrowser.ts +239 -82
- package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
- package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
- package/src/web/TrackPlayer/Player.load.test.ts +137 -1
- package/src/web/TrackPlayer/Player.ts +284 -37
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
- package/src/web/__tests__/lifecycle.test.ts +345 -0
- package/src/web/__tests__/lifecycleHarness.ts +351 -0
- package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
- package/src/web/__tests__/loadSupersession.test.ts +175 -0
- package/src/web/__tests__/retry.test.ts +169 -0
- package/src/web/browser/BrowserManager.ts +209 -111
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/SearchManager.test.ts +75 -0
- package/src/web/browser/SearchManager.ts +25 -28
- package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
- package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
- package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
- package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
- 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/http/RequestConfigBuilder.test.ts +241 -1
- package/src/web/http/RequestConfigBuilder.ts +140 -33
- package/src/web/http/followMediaRedirect.test.ts +289 -0
- package/src/web/http/followMediaRedirect.ts +253 -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 +247 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/ios/Model/MediaURL.swift +0 -67
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- 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/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- 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/NotificationButtonLayout.swift +0 -92
- 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
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
|
@@ -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,46 @@ object TrackFactory {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* the
|
|
33
|
-
*
|
|
34
|
-
*
|
|
28
|
+
* A synthetic browsable Track for a section's "view more" row — the section has a path but no
|
|
29
|
+
* Track to hang it on (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.
|
|
33
|
+
*
|
|
34
|
+
* [title] is the consumer's `viewMoreTitle`, one string for every section — not the section's own
|
|
35
|
+
* title, which the group header above the row already shows.
|
|
35
36
|
*/
|
|
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 =
|
|
37
|
+
fun navigationTrack(section: Section, title: String): Track =
|
|
50
38
|
Track(
|
|
51
|
-
id =
|
|
52
|
-
|
|
53
|
-
src =
|
|
54
|
-
artwork =
|
|
55
|
-
artworkSource =
|
|
56
|
-
request =
|
|
57
|
-
artworkCarPlayTinted = null,
|
|
39
|
+
id = null,
|
|
40
|
+
path = section.path,
|
|
41
|
+
src = null,
|
|
42
|
+
artwork = null,
|
|
43
|
+
artworkSource = null,
|
|
44
|
+
request = null,
|
|
58
45
|
title = title,
|
|
59
46
|
subtitle = null,
|
|
60
|
-
artist =
|
|
61
|
-
|
|
62
|
-
album =
|
|
47
|
+
artist = null,
|
|
48
|
+
albumPath = null,
|
|
49
|
+
album = null,
|
|
63
50
|
description = null,
|
|
64
51
|
genre = null,
|
|
65
52
|
duration = null,
|
|
66
|
-
style =
|
|
67
|
-
|
|
53
|
+
style =
|
|
54
|
+
section.style?.let {
|
|
55
|
+
TrackStyle(
|
|
56
|
+
display = it.display,
|
|
57
|
+
artworkRendering = it.artworkRendering,
|
|
58
|
+
imageShape = it.imageShape,
|
|
59
|
+
accessorySymbol = it.accessorySymbol,
|
|
60
|
+
cardTint = it.cardTint,
|
|
61
|
+
cardImage = it.cardImage,
|
|
62
|
+
)
|
|
63
|
+
},
|
|
64
|
+
disabled = null,
|
|
68
65
|
favorited = null,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
imageRow = null,
|
|
66
|
+
live = null,
|
|
67
|
+
icyCharset = null,
|
|
72
68
|
)
|
|
73
69
|
|
|
74
70
|
fun toMedia3(tracks: Array<Track>): List<MediaItem> {
|
|
@@ -90,12 +86,14 @@ object TrackFactory {
|
|
|
90
86
|
track: Track,
|
|
91
87
|
registry: BrowseArtworkRegistry,
|
|
92
88
|
authority: String,
|
|
89
|
+
section: Section? = null,
|
|
93
90
|
): MediaItem {
|
|
94
91
|
val rawUrl = artworkUri(track) // artworkSource.uri ?: artwork
|
|
95
92
|
val scheme = rawUrl?.let { Uri.parse(it).scheme?.lowercase() }
|
|
96
|
-
val builder = metadataBuilder(track)
|
|
93
|
+
val builder = metadataBuilder(track, section)
|
|
97
94
|
if (rawUrl != null && (scheme == "http" || scheme == "https")) {
|
|
98
|
-
val isSvg =
|
|
95
|
+
val isSvg =
|
|
96
|
+
SvgArtworkRenderer.isSvgUrl(rawUrl) || SvgArtworkRenderer.isSvgUrl(track.artwork?.url)
|
|
99
97
|
val token = ArtworkUris.tokenFor(rawUrl)
|
|
100
98
|
registry.register(token, ResolvedArtwork(rawUrl, track.artworkSource?.headers, isSvg))
|
|
101
99
|
builder.setArtworkUri(ArtworkUris.contentUri(authority, token).toUri())
|
|
@@ -108,8 +106,11 @@ object TrackFactory {
|
|
|
108
106
|
/** The transformed artworkSource wins over the raw artwork field. */
|
|
109
107
|
private fun artworkUri(track: Track): String? = track.artworkSource?.uri ?: track.artwork?.url
|
|
110
108
|
|
|
111
|
-
/**
|
|
112
|
-
|
|
109
|
+
/**
|
|
110
|
+
* All metadata except artwork. The owning [section] (browse surfaces only) supplies the
|
|
111
|
+
* group-title header hint and the per-item style default (ADR 0010).
|
|
112
|
+
*/
|
|
113
|
+
private fun metadataBuilder(track: Track, section: Section? = null): MediaMetadata.Builder =
|
|
113
114
|
MediaMetadata.Builder()
|
|
114
115
|
.setTitle(track.title)
|
|
115
116
|
.setArtist(track.subtitle)
|
|
@@ -118,26 +119,29 @@ object TrackFactory {
|
|
|
118
119
|
.setGenre(track.genre)
|
|
119
120
|
.setIsBrowsable(track.src == null)
|
|
120
121
|
.setIsPlayable(track.src != null)
|
|
121
|
-
.setExtras(MediaExtrasBuilder.build(track))
|
|
122
|
+
.setExtras(MediaExtrasBuilder.build(track, section))
|
|
122
123
|
.apply { track.favorited?.let { setUserRating(HeartRating(it)) } }
|
|
123
124
|
|
|
124
125
|
private fun buildMediaItem(track: Track, metadata: MediaMetadata): MediaItem {
|
|
125
|
-
// A playable track's mediaId is the consumer's stable `id` when it has one
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// `
|
|
126
|
+
// A playable track's mediaId is the consumer's stable `id` when it has one —
|
|
127
|
+
// the identity rule (id when non-blank, else src; see Track.identity /
|
|
128
|
+
// ADR 0008), except that an id-less playable track prefers its contextual
|
|
129
|
+
// `path` over `src` here, so this is deliberately NOT expressed via
|
|
130
|
+
// Track.identity. Android Auto marks the "now playing" browse row by exact
|
|
131
|
+
// mediaId equality between the row and the player's current item (media3
|
|
132
|
+
// announces `currentMediaItem.mediaId` in the legacy playback state), and a
|
|
129
133
|
// consumer-loaded track's `src` can differ textually from the browse row's
|
|
130
134
|
// for the same item (absolute vs relative, extra query params) — the id is
|
|
131
135
|
// the one string both sides share. It is also context-free, so the same
|
|
132
136
|
// station gets the same mediaId in every tab. Browsable-only tracks keep
|
|
133
|
-
// `
|
|
137
|
+
// `path` so navigation parentIds stay resolvable paths.
|
|
134
138
|
val stableId = if (track.src != null) track.id?.takeUnless { it.isBlank() } else null
|
|
135
139
|
val mediaId =
|
|
136
140
|
stableId
|
|
137
|
-
?: track.
|
|
141
|
+
?: track.path
|
|
138
142
|
?: track.src
|
|
139
143
|
?: throw IllegalArgumentException(
|
|
140
|
-
"Track must have either
|
|
144
|
+
"Track must have either path or src defined. Track: title='${track.title}', artist='${track.artist}'"
|
|
141
145
|
)
|
|
142
146
|
return MediaItem.Builder()
|
|
143
147
|
.setMediaId(mediaId)
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Default copy for the two error tiles the library invents in the Android Auto
|
|
4
|
+
browse list. These rows have no consumer-authored Track behind them, so there
|
|
5
|
+
is nothing in the browse tree to read a title from; English here because a
|
|
6
|
+
default that renders beats a blank row.
|
|
7
|
+
|
|
8
|
+
Resources rather than a browser-config option — the route the library's
|
|
9
|
+
cross-platform copy takes (`formatNavigationError`, `carPlayLoadingTitle`,
|
|
10
|
+
`viewMoreTitle`) — because these two are not cross-platform. They exist only
|
|
11
|
+
because Media3 drops the message from `LibraryResult.ofError` on the legacy
|
|
12
|
+
browse bridge, so a row is the sole signal available; CarPlay has no
|
|
13
|
+
counterpart, stating failures in the list's centered empty view instead
|
|
14
|
+
(CarPlayController.showMessage). Android-only copy in the Android-idiomatic
|
|
15
|
+
place: an app re-skins either by declaring the same name in its own
|
|
16
|
+
strings.xml, and a `values-<locale>/` file can add translations later without
|
|
17
|
+
touching the API.
|
|
18
|
+
-->
|
|
2
19
|
<resources>
|
|
3
|
-
<!--
|
|
20
|
+
<!--
|
|
21
|
+
The SessionError message a browse or search failure sends alongside its
|
|
22
|
+
tile. The tiles themselves are worded by the app's `formatNavigationError`
|
|
23
|
+
(ADR 0001), so these two are the library's only device-locale browse copy —
|
|
24
|
+
kept because a SessionError carries a string, not a navigation error.
|
|
25
|
+
Offline is distinct from the browse error: it is sent only when the network
|
|
26
|
+
monitor says we are actually offline, so it may name the cause.
|
|
27
|
+
-->
|
|
4
28
|
<string name="audio_browser_offline_error">No internet connection</string>
|
|
5
|
-
<string name="audio_browser_offline_error_subtitle">Check your connection and try again</string>
|
|
6
|
-
|
|
7
|
-
<!-- Generic error message shown in Android Auto when browsing fails -->
|
|
8
29
|
<string name="audio_browser_browse_error">Something went wrong</string>
|
|
9
|
-
<string name="audio_browser_browse_error_subtitle">Please try again later</string>
|
|
10
30
|
</resources>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
package com.audiobrowser
|
|
2
|
+
|
|
3
|
+
import android.content.ComponentName
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.content.ServiceConnection
|
|
6
|
+
import android.os.IBinder
|
|
7
|
+
import org.junit.Assert.assertEquals
|
|
8
|
+
import org.junit.Assert.assertFalse
|
|
9
|
+
import org.junit.Assert.assertTrue
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
import org.robolectric.RobolectricTestRunner
|
|
13
|
+
import org.robolectric.RuntimeEnvironment
|
|
14
|
+
import org.robolectric.Shadows.shadowOf
|
|
15
|
+
|
|
16
|
+
// Robolectric: bindService/unbindService only exist under the framework, and ShadowApplication
|
|
17
|
+
// records both sides — which is precisely the leak being guarded against.
|
|
18
|
+
@RunWith(RobolectricTestRunner::class)
|
|
19
|
+
class ServiceBindingTest {
|
|
20
|
+
|
|
21
|
+
private val app = RuntimeEnvironment.getApplication()
|
|
22
|
+
private val intent = Intent(app, Service::class.java)
|
|
23
|
+
|
|
24
|
+
private val connection =
|
|
25
|
+
object : ServiceConnection {
|
|
26
|
+
override fun onServiceConnected(name: ComponentName?, service: IBinder?) = Unit
|
|
27
|
+
|
|
28
|
+
override fun onServiceDisconnected(name: ComponentName?) = Unit
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Both the auto-bind at construction and setup's bind reach the same object. */
|
|
32
|
+
@Test
|
|
33
|
+
fun bindingTwiceTakesOnlyOneBinding() {
|
|
34
|
+
val binding = ServiceBinding(app)
|
|
35
|
+
|
|
36
|
+
assertTrue(binding.bind(intent, connection))
|
|
37
|
+
assertTrue(binding.bind(intent, connection))
|
|
38
|
+
|
|
39
|
+
assertEquals(1, shadowOf(app).boundServiceConnections.size)
|
|
40
|
+
|
|
41
|
+
binding.unbind(connection)
|
|
42
|
+
|
|
43
|
+
assertTrue(shadowOf(app).boundServiceConnections.isEmpty())
|
|
44
|
+
assertEquals(1, shadowOf(app).unboundServiceConnections.size)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** dispose() must not throw, and Android throws when unbinding a connection it never had. */
|
|
48
|
+
@Test
|
|
49
|
+
fun unbindWithoutBindIsANoOp() {
|
|
50
|
+
val binding = ServiceBinding(app)
|
|
51
|
+
|
|
52
|
+
binding.unbind(connection)
|
|
53
|
+
|
|
54
|
+
assertFalse(binding.isBound)
|
|
55
|
+
assertTrue(shadowOf(app).unboundServiceConnections.isEmpty())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Android registers the connection before it asks the ActivityManager, so a false return still
|
|
60
|
+
* leaves a binding to release. Treating false as "nothing bound" strands it permanently — the
|
|
61
|
+
* leak this class exists to prevent.
|
|
62
|
+
*/
|
|
63
|
+
@Test
|
|
64
|
+
fun failedBindStillHoldsAConnectionToRelease() {
|
|
65
|
+
val binding = ServiceBinding(app)
|
|
66
|
+
shadowOf(app).declareActionUnbindable("unbindable")
|
|
67
|
+
|
|
68
|
+
assertFalse(binding.bind(Intent("unbindable"), connection))
|
|
69
|
+
|
|
70
|
+
assertTrue(binding.isBound)
|
|
71
|
+
|
|
72
|
+
binding.unbind(connection)
|
|
73
|
+
|
|
74
|
+
assertEquals(1, shadowOf(app).unboundServiceConnections.size)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Test
|
|
78
|
+
fun unbindIsIdempotent() {
|
|
79
|
+
val binding = ServiceBinding(app)
|
|
80
|
+
binding.bind(intent, connection)
|
|
81
|
+
|
|
82
|
+
binding.unbind(connection)
|
|
83
|
+
binding.unbind(connection)
|
|
84
|
+
|
|
85
|
+
assertEquals(1, shadowOf(app).unboundServiceConnections.size)
|
|
86
|
+
}
|
|
87
|
+
}
|