react-native-audio-browser 0.1.1 → 0.2.0-next.10
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/AudioBrowser.podspec +1 -1
- package/README.md +184 -2
- package/android/build.gradle +17 -5
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +18 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +473 -158
- 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/Callbacks.kt +4 -6
- package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +442 -178
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +299 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +42 -0
- package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
- package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
- package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +40 -46
- package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +172 -0
- package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
- package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
- package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +422 -134
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +248 -404
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +347 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +25 -5
- package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
- package/android/src/main/java/com/audiobrowser/player/Player.kt +475 -577
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +118 -53
- package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
- package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
- package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
- package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +85 -40
- package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +156 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +109 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +55 -0
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +265 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
- package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
- package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
- package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
- package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
- package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
- package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
- package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
- package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
- package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +112 -80
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +165 -0
- package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +203 -0
- package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
- package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +190 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
- package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
- package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
- package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +186 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +158 -0
- package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
- package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
- package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
- package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
- package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
- package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
- package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
- package/ios/Browser/BrowserConfig.swift +41 -11
- package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
- package/ios/Browser/BrowserManager.swift +303 -135
- package/ios/Browser/BrowserPathHelper.swift +32 -3
- package/ios/Browser/JsonModels.swift +287 -66
- package/ios/Browser/MediaResolveComposer.swift +114 -0
- package/ios/Browser/SearchDrillIn.swift +44 -0
- package/ios/Browser/SectionScope.swift +35 -0
- package/ios/Browser/TabBarEntries.swift +15 -0
- package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
- package/ios/CarPlay/CarPlayController.swift +739 -149
- package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
- package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
- package/ios/CarPlay/RNABAudioBrowser.h +15 -10
- package/ios/CarPlay/RNABAudioBrowser.m +15 -14
- package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
- package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
- package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
- package/ios/Extension/Capability+RemoteCommand.swift +7 -9
- package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/NavigationError+Conversion.swift +8 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
- package/ios/Extension/RequestConfig+Copying.swift +31 -0
- package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
- package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Track+AVPlayer.swift +4 -4
- package/ios/Extension/Track+Copying.swift +9 -3
- package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Variant+Artwork.swift +49 -0
- package/ios/HybridAudioBrowser.swift +805 -197
- package/ios/Model/NitroTypeStubs.swift +299 -224
- package/ios/Model/PlayerUpdateOptions.swift +23 -23
- package/ios/Model/RemoteCommand.swift +24 -6
- package/ios/Model/TrackPlayerError.swift +82 -18
- package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
- package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
- package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
- package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
- package/ios/Observer/PlayerTimeObserver.swift +3 -53
- package/ios/Option/SessionCategories.swift +34 -11
- package/ios/Player/AirPlayStallJudgement.swift +25 -0
- package/ios/Player/EndOfTrackJudgement.swift +32 -0
- package/ios/Player/LoadSeekCoordinator.swift +16 -1
- package/ios/Player/MediaIntentCriteria.swift +139 -0
- package/ios/Player/MediaLoader.swift +31 -11
- package/ios/Player/NowPlayingUpdater.swift +119 -26
- package/ios/Player/PersistedPlaybackState.swift +11 -0
- package/ios/Player/PlaybackCoordinator.swift +285 -51
- package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
- package/ios/Player/PlaybackEffectHandler.swift +19 -9
- package/ios/Player/PlaybackErrorHandler.swift +98 -14
- package/ios/Player/PlaybackStateMachine.swift +18 -13
- package/ios/Player/PlaybackStateStore.swift +27 -0
- package/ios/Player/PlaybackTimer.swift +61 -0
- package/ios/Player/PlayingStateManager.swift +3 -2
- package/ios/Player/QueueManager.swift +77 -64
- package/ios/Player/RetryManager.swift +175 -38
- package/ios/Player/ShuffleOrder.swift +10 -0
- package/ios/Player/SleepTimerHandling.swift +8 -0
- package/ios/Player/SleepTimerManager.swift +72 -11
- package/ios/Player/VolumeFader.swift +77 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +40 -21
- package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
- package/ios/Tests/BrowserPathHelperTests.swift +28 -0
- package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
- package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
- package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
- package/ios/Tests/JsonModelsTests.swift +58 -0
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +40 -7
- package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
- package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
- package/ios/Tests/MediaLoaderTests.swift +32 -20
- package/ios/Tests/MediaResolveTests.swift +126 -0
- package/ios/Tests/OnceValueTests.swift +79 -0
- package/ios/Tests/PlaybackCoordinatorTests.swift +787 -13
- package/ios/Tests/PlaybackErrorHandlerTests.swift +383 -47
- package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
- package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
- package/ios/Tests/PlaybackTimerTests.swift +55 -0
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/PlayingStateManagerTests.swift +57 -0
- package/ios/Tests/QueueManagerTests.swift +195 -18
- package/ios/Tests/RemoteCommandControllerTests.swift +156 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
- package/ios/Tests/RetryManagerTests.swift +178 -0
- package/ios/Tests/SearchDrillInTests.swift +63 -0
- package/ios/Tests/SectionScopeTests.swift +82 -0
- package/ios/Tests/SleepTimerManagerTests.swift +107 -0
- package/ios/Tests/Support/ManualSleepTimerScheduler.swift +47 -0
- package/ios/Tests/Support/TestTypes.swift +61 -20
- package/ios/Tests/TabBarEntriesTests.swift +71 -0
- package/ios/Tests/TrackSelectorTests.swift +15 -15
- package/ios/TrackPlayer.swift +385 -58
- package/ios/TrackPlayerCallbacks.swift +5 -44
- package/ios/TrackSelector.swift +5 -5
- package/ios/Util/ArtworkImageFetcher.swift +4 -2
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +7 -3
- package/ios/Util/OnceValue.swift +34 -0
- package/lib/commonjs/AudioBrowser.js +0 -11
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +227 -0
- package/lib/commonjs/features/browser-config.js.map +1 -0
- package/lib/commonjs/features/browser.js +60 -159
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +42 -0
- package/lib/commonjs/features/carConnection.js.map +1 -0
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +37 -5
- package/lib/commonjs/features/errors.js.map +1 -1
- package/lib/commonjs/features/favorites.js +1 -1
- package/lib/commonjs/features/gate.js +164 -0
- package/lib/commonjs/features/gate.js.map +1 -0
- package/lib/commonjs/features/index.js +22 -0
- package/lib/commonjs/features/index.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 +36 -1
- package/lib/commonjs/features/nowPlaying.js.map +1 -1
- package/lib/commonjs/features/output.js +37 -23
- package/lib/commonjs/features/output.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +9 -0
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/index.js +11 -0
- package/lib/commonjs/features/playback/index.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 +29 -13
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +86 -0
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/commonjs/features/playback/volume.js +2 -2
- package/lib/commonjs/features/player/options.js +79 -29
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +256 -3
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +21 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +1 -1
- package/lib/commonjs/features/queue/queue.js +12 -5
- 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 +14 -104
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +22 -5
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/native.js +5 -0
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.web.js +4 -1
- package/lib/commonjs/native.web.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 +8 -8
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/artwork.js +31 -0
- package/lib/commonjs/utils/artwork.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +302 -125
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/index.js +13 -5
- package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +176 -50
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +11 -9
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/commonjs/web/http/HttpClient.js +4 -23
- package/lib/commonjs/web/http/HttpClient.js.map +1 -1
- package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/playbackErrorKind.js +57 -0
- package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
- package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
- package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/player/OptionsManager.js +9 -17
- package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
- package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
- package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
- package/lib/module/AudioBrowser.js +1 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +221 -0
- package/lib/module/features/browser-config.js.map +1 -0
- package/lib/module/features/browser.js +54 -157
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +37 -0
- package/lib/module/features/carConnection.js.map +1 -0
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +37 -5
- package/lib/module/features/errors.js.map +1 -1
- package/lib/module/features/favorites.js +1 -1
- package/lib/module/features/gate.js +159 -0
- package/lib/module/features/gate.js.map +1 -0
- package/lib/module/features/index.js +2 -0
- package/lib/module/features/index.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 +34 -1
- package/lib/module/features/nowPlaying.js.map +1 -1
- package/lib/module/features/output.js +32 -19
- package/lib/module/features/output.js.map +1 -1
- package/lib/module/features/playback/controls.js +8 -0
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/index.js +1 -0
- package/lib/module/features/playback/index.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 +29 -13
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/state.js +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +83 -0
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/module/features/playback/volume.js +2 -2
- package/lib/module/features/player/options.js +79 -29
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +256 -3
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +17 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +1 -1
- package/lib/module/features/queue/queue.js +12 -5
- 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 -103
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +22 -5
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/native.js +5 -0
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.web.js +4 -1
- package/lib/module/native.web.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 +8 -8
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +0 -3
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/artwork.js +26 -0
- package/lib/module/utils/artwork.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +303 -126
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/module/web/TrackPlayer/Player.js +90 -26
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
- package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/module/web/TrackPlayer/index.js +2 -1
- package/lib/module/web/TrackPlayer/index.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +176 -50
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/NavigationErrorManager.js +3 -1
- package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +11 -9
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/module/web/browser/parseSearchResponse.js +16 -0
- package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/module/web/http/HttpClient.js +4 -23
- package/lib/module/web/http/HttpClient.js.map +1 -1
- package/lib/module/web/http/RequestConfigBuilder.js +107 -58
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/playbackErrorKind.js +52 -0
- package/lib/module/web/playbackErrorKind.js.map +1 -0
- package/lib/module/web/player/MediaSessionManager.js +116 -0
- package/lib/module/web/player/MediaSessionManager.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +7 -5
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/player/OptionsManager.js +9 -17
- package/lib/module/web/player/OptionsManager.js.map +1 -1
- package/lib/module/web/player/RemoteCommandController.js +92 -0
- package/lib/module/web/player/RemoteCommandController.js.map +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +0 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts +12 -0
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
- package/lib/typescript/src/features/browser.d.ts +40 -1
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +20 -0
- package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +61 -6
- package/lib/typescript/src/features/errors.d.ts.map +1 -1
- package/lib/typescript/src/features/favorites.d.ts +1 -1
- package/lib/typescript/src/features/gate.d.ts +93 -0
- package/lib/typescript/src/features/gate.d.ts.map +1 -0
- package/lib/typescript/src/features/index.d.ts +2 -0
- package/lib/typescript/src/features/index.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +26 -7
- 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 +28 -1
- package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
- package/lib/typescript/src/features/output.d.ts +28 -18
- package/lib/typescript/src/features/output.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +5 -0
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/index.d.ts +1 -0
- package/lib/typescript/src/features/playback/index.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 +4 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
- package/lib/typescript/src/features/playback/volume.d.ts +2 -2
- package/lib/typescript/src/features/player/options.d.ts +176 -127
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +216 -167
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +9 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +11 -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 -35
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +19 -4
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native.d.ts +1 -2
- package/lib/typescript/src/native.d.ts.map +1 -1
- package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
- package/lib/typescript/src/specs/audio-browser.nitro.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 +9 -3
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +349 -85
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +8 -6
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/artwork.d.ts +19 -0
- package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
- package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +4 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
- package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
- package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
- package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -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 +131 -0
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +14 -9
- package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
- package/nitrogen/generated/android/c++/JGate.hpp +62 -0
- package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
- package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
- package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +247 -223
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +26 -23
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
- package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
- package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
- package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +21 -24
- package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
- package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
- package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +19 -14
- package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
- package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
- package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
- package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
- package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +20 -20
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
- package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
- package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
- package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
- package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
- 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_.cpp +41 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → FavoriteConfig.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.kt → Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt} +12 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → Gate.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → GateReason.kt} +5 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +104 -110
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButton.kt +26 -0
- 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 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1126 -490
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +66 -45
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +178 -120
- package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -22
- package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
- package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
- package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
- package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
- package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.swift +77 -0
- package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
- package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
- package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
- package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
- package/nitrogen/generated/ios/swift/{Func_void_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError___.swift → Func_void_std__shared_ptr_Promise_GateDecision__.swift} +17 -17
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
- package/nitrogen/generated/ios/swift/Gate.swift +47 -0
- package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
- package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
- package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +21 -15
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +232 -199
- package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
- package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
- package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
- package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
- package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
- package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
- package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
- package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
- package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -41
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
- package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
- package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
- package/nitrogen/generated/ios/swift/Options.swift +10 -4
- package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
- package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
- package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
- package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
- package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +10 -10
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
- package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
- package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
- package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
- package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +74 -8
- package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
- package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
- package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
- package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
- package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
- package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
- package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
- package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
- package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
- package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
- package/nitrogen/generated/shared/c++/{PercentageRating.hpp → Gate.hpp} +21 -16
- package/nitrogen/generated/shared/c++/{HeartRating.hpp → GateDecision.hpp} +23 -17
- package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateEvent.hpp} +18 -17
- package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +22 -19
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +45 -36
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
- package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
- package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
- package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
- package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
- package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
- package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
- package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
- package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
- package/nitrogen/generated/shared/c++/Options.hpp +8 -8
- package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
- package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
- package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
- package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
- package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +25 -25
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
- package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
- package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
- package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
- package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +24 -5
- package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
- package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
- package/package.json +20 -8
- package/src/AudioBrowser.ts +1 -1
- package/src/CLAUDE.md +52 -0
- package/src/codegen-no-config-variant.test.ts +70 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +110 -0
- package/src/features/browser-config.ts +293 -0
- package/src/features/browser.test.ts +19 -0
- package/src/features/browser.ts +59 -202
- package/src/features/carConnection.ts +36 -0
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +71 -5
- package/src/features/favorites.ts +1 -1
- package/src/features/gate.test.ts +72 -0
- package/src/features/gate.ts +170 -0
- package/src/features/hook-wiring.test.ts +254 -0
- package/src/features/index.ts +2 -0
- package/src/features/metadata.ts +26 -15
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +37 -1
- package/src/features/output.ts +35 -22
- package/src/features/playback/controls.ts +8 -0
- package/src/features/playback/index.ts +1 -0
- 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 +32 -13
- package/src/features/playback/state.ts +1 -1
- package/src/features/playback/trackPlaybackTime.test.ts +76 -0
- package/src/features/playback/trackPlaybackTime.ts +101 -0
- package/src/features/playback/volume.ts +2 -2
- package/src/features/player/options.test.ts +38 -0
- package/src/features/player/options.ts +217 -110
- package/src/features/player/setup.test.ts +125 -0
- package/src/features/player/setup.ts +400 -175
- package/src/features/player/validateOptions.ts +19 -0
- package/src/features/queue/activeTrack.ts +1 -1
- package/src/features/queue/queue.test.ts +36 -0
- package/src/features/queue/queue.ts +17 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -115
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +32 -5
- package/src/native.ts +5 -0
- package/src/native.web.ts +4 -1
- package/src/public-surface.test.ts +107 -0
- package/src/specs/audio-browser.nitro.ts +132 -42
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +19 -6
- package/src/types/browser-nodes.ts +160 -13
- package/src/types/browser.ts +377 -89
- package/src/utils/LazyNativeEmitter.ts +9 -8
- package/src/utils/NativeUpdatedValue.ts +0 -2
- package/src/utils/artwork.ts +30 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
- package/src/web/NativeAudioBrowser.ts +366 -159
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
- package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
- package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
- package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
- package/src/web/TrackPlayer/Player.load.test.ts +86 -0
- package/src/web/TrackPlayer/Player.ts +113 -39
- package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
- package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
- package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
- package/src/web/TrackPlayer/QueueManager.ts +302 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
- package/src/web/TrackPlayer/RepeatMode.ts +1 -1
- package/src/web/TrackPlayer/SleepTimer.ts +38 -3
- package/src/web/TrackPlayer/VolumeFader.ts +74 -0
- package/src/web/TrackPlayer/index.ts +2 -1
- package/src/web/browser/BrowserManager.ts +231 -62
- package/src/web/browser/NavigationErrorManager.ts +3 -1
- package/src/web/browser/SearchManager.ts +11 -15
- package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
- package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
- package/src/web/browser/assertBrowsePageShape.ts +27 -0
- package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
- package/src/web/browser/classifyTrackNavigation.ts +35 -0
- package/src/web/browser/parseSearchResponse.test.ts +25 -0
- package/src/web/browser/parseSearchResponse.ts +15 -0
- package/src/web/http/HttpClient.ts +5 -35
- package/src/web/http/RequestConfigBuilder.test.ts +59 -0
- package/src/web/http/RequestConfigBuilder.ts +120 -63
- package/src/web/playbackErrorKind.test.ts +55 -0
- package/src/web/playbackErrorKind.ts +56 -0
- package/src/web/player/MediaSessionManager.test.ts +220 -0
- package/src/web/player/MediaSessionManager.ts +151 -0
- package/src/web/player/NowPlayingManager.test.ts +46 -0
- package/src/web/player/NowPlayingManager.ts +6 -5
- package/src/web/player/OptionsManager.ts +12 -18
- package/src/web/player/RemoteCommandController.test.ts +171 -0
- package/src/web/player/RemoteCommandController.ts +166 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
- package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
- package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
- package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
- package/ios/CarPlay/Track+CarPlay.swift +0 -63
- package/ios/Model/MediaURL.swift +0 -67
- package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
- package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
- package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
- package/lib/commonjs/features/rating.js +0 -2
- package/lib/commonjs/features/rating.js.map +0 -1
- package/lib/commonjs/types/player.js +0 -2
- package/lib/commonjs/types/player.js.map +0 -1
- package/lib/commonjs/web/SimpleRouter.test.js +0 -174
- package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/module/features/rating.js +0 -2
- package/lib/module/features/rating.js.map +0 -1
- package/lib/module/types/player.js +0 -2
- package/lib/module/types/player.js.map +0 -1
- package/lib/module/web/SimpleRouter.test.js +0 -174
- package/lib/module/web/SimpleRouter.test.js.map +0 -1
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/typescript/src/features/rating.d.ts +0 -14
- package/lib/typescript/src/features/rating.d.ts.map +0 -1
- package/lib/typescript/src/native.web.d.ts +0 -3
- package/lib/typescript/src/native.web.d.ts.map +0 -1
- package/lib/typescript/src/types/player.d.ts +0 -64
- package/lib/typescript/src/types/player.d.ts.map +0 -1
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
- package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
- package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
- package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
- package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
- package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
- package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
- package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
- package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
- package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
- package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
- package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
- package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
- package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
- package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
- package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
- package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
- package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
- package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
- 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/AndroidUpdateOptions.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
- package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
- package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
- package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
- package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
- package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
- package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
- package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
- package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
- package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
- package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
- package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
- package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
- package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
- package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
- package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
- package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
- package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
- package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
- package/src/features/rating.ts +0 -17
- package/src/types/player.ts +0 -69
- package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
|
@@ -8,6 +8,7 @@ import android.os.Handler
|
|
|
8
8
|
import android.os.IBinder
|
|
9
9
|
import android.os.Looper
|
|
10
10
|
import androidx.annotation.Keep
|
|
11
|
+
import androidx.car.app.connection.CarConnection
|
|
11
12
|
import androidx.lifecycle.DefaultLifecycleObserver
|
|
12
13
|
import androidx.lifecycle.LifecycleOwner
|
|
13
14
|
import androidx.lifecycle.ProcessLifecycleOwner
|
|
@@ -15,38 +16,41 @@ import androidx.media3.session.MediaBrowser
|
|
|
15
16
|
import androidx.media3.session.SessionToken
|
|
16
17
|
import com.audiobrowser.browser.BrowserConfig
|
|
17
18
|
import com.audiobrowser.browser.BrowserManager
|
|
18
|
-
import com.audiobrowser.browser.handleTrackLoad
|
|
19
19
|
import com.audiobrowser.browser.CallbackException
|
|
20
20
|
import com.audiobrowser.browser.ContentNotFoundException
|
|
21
21
|
import com.audiobrowser.browser.HttpStatusException
|
|
22
22
|
import com.audiobrowser.browser.NetworkException
|
|
23
|
+
import com.audiobrowser.browser.handleTrackLoad
|
|
24
|
+
import com.audiobrowser.browser.resolveMediaUrl
|
|
23
25
|
import com.audiobrowser.extension.NumberExt.Companion.toSeconds
|
|
24
|
-
import com.audiobrowser.http.RequestConfigBuilder
|
|
25
|
-
import com.audiobrowser.model.PlaybackMetadata
|
|
26
26
|
import com.audiobrowser.model.PlayerSetupOptions
|
|
27
27
|
import com.audiobrowser.model.PlayerUpdateOptions
|
|
28
|
+
import com.audiobrowser.player.Player
|
|
28
29
|
import com.audiobrowser.util.BatteryOptimizationHelper
|
|
29
30
|
import com.audiobrowser.util.BatteryWarningStore
|
|
30
31
|
import com.audiobrowser.util.BrowserPathHelper
|
|
31
|
-
import com.audiobrowser.util.
|
|
32
|
+
import com.audiobrowser.util.OutputMonitor
|
|
33
|
+
import com.audiobrowser.util.OutputSwitcher
|
|
32
34
|
import com.audiobrowser.util.SystemVolumeMonitor
|
|
33
35
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
34
36
|
import com.google.common.util.concurrent.ListenableFuture
|
|
35
37
|
import com.margelo.nitro.NitroModules
|
|
36
|
-
import com.margelo.nitro.audiobrowser.ChapterMetadata
|
|
37
|
-
import com.margelo.nitro.audiobrowser.TimedMetadata
|
|
38
|
-
import com.margelo.nitro.audiobrowser.TrackMetadata
|
|
39
38
|
import com.margelo.nitro.audiobrowser.BatteryOptimizationStatus
|
|
40
39
|
import com.margelo.nitro.audiobrowser.BatteryOptimizationStatusChangedEvent
|
|
41
40
|
import com.margelo.nitro.audiobrowser.BatteryWarningPendingChangedEvent
|
|
41
|
+
import com.margelo.nitro.audiobrowser.ChapterMetadata
|
|
42
42
|
import com.margelo.nitro.audiobrowser.EqualizerSettings
|
|
43
43
|
import com.margelo.nitro.audiobrowser.FavoriteChangedEvent
|
|
44
44
|
import com.margelo.nitro.audiobrowser.FormatNavigationErrorParams
|
|
45
45
|
import com.margelo.nitro.audiobrowser.FormattedNavigationError
|
|
46
|
+
import com.margelo.nitro.audiobrowser.Gate
|
|
47
|
+
import com.margelo.nitro.audiobrowser.GateDecision
|
|
48
|
+
import com.margelo.nitro.audiobrowser.GateEvent
|
|
46
49
|
import com.margelo.nitro.audiobrowser.HybridAudioBrowserSpec
|
|
47
|
-
import com.margelo.nitro.audiobrowser.IosOutput
|
|
48
50
|
import com.margelo.nitro.audiobrowser.MediaRequestConfig
|
|
49
51
|
import com.margelo.nitro.audiobrowser.NativeBrowserConfiguration
|
|
52
|
+
import com.margelo.nitro.audiobrowser.NativeGateRequest
|
|
53
|
+
import com.margelo.nitro.audiobrowser.NativeSetupPlayerOptions
|
|
50
54
|
import com.margelo.nitro.audiobrowser.NativeUpdateOptions
|
|
51
55
|
import com.margelo.nitro.audiobrowser.NavigationError
|
|
52
56
|
import com.margelo.nitro.audiobrowser.NavigationErrorEvent
|
|
@@ -54,7 +58,7 @@ import com.margelo.nitro.audiobrowser.NavigationErrorType
|
|
|
54
58
|
import com.margelo.nitro.audiobrowser.NowPlayingMetadata
|
|
55
59
|
import com.margelo.nitro.audiobrowser.NowPlayingUpdate
|
|
56
60
|
import com.margelo.nitro.audiobrowser.Options
|
|
57
|
-
import com.margelo.nitro.audiobrowser.
|
|
61
|
+
import com.margelo.nitro.audiobrowser.Output
|
|
58
62
|
import com.margelo.nitro.audiobrowser.Playback
|
|
59
63
|
import com.margelo.nitro.audiobrowser.PlaybackActiveTrackChangedEvent
|
|
60
64
|
import com.margelo.nitro.audiobrowser.PlaybackError
|
|
@@ -62,6 +66,7 @@ import com.margelo.nitro.audiobrowser.PlaybackErrorEvent
|
|
|
62
66
|
import com.margelo.nitro.audiobrowser.PlaybackPlayWhenReadyChangedEvent
|
|
63
67
|
import com.margelo.nitro.audiobrowser.PlaybackProgressUpdatedEvent
|
|
64
68
|
import com.margelo.nitro.audiobrowser.PlaybackQueueEndedEvent
|
|
69
|
+
import com.margelo.nitro.audiobrowser.PlaybackState
|
|
65
70
|
import com.margelo.nitro.audiobrowser.PlayingState
|
|
66
71
|
import com.margelo.nitro.audiobrowser.Progress
|
|
67
72
|
import com.margelo.nitro.audiobrowser.RemoteJumpBackwardEvent
|
|
@@ -69,25 +74,25 @@ import com.margelo.nitro.audiobrowser.RemoteJumpForwardEvent
|
|
|
69
74
|
import com.margelo.nitro.audiobrowser.RemotePlayIdEvent
|
|
70
75
|
import com.margelo.nitro.audiobrowser.RemotePlaySearchEvent
|
|
71
76
|
import com.margelo.nitro.audiobrowser.RemoteSeekEvent
|
|
72
|
-
import com.margelo.nitro.audiobrowser.RemoteSetRatingEvent
|
|
73
77
|
import com.margelo.nitro.audiobrowser.RemoteSkipEvent
|
|
74
78
|
import com.margelo.nitro.audiobrowser.RepeatMode
|
|
75
79
|
import com.margelo.nitro.audiobrowser.RepeatModeChangedEvent
|
|
76
|
-
import com.margelo.nitro.audiobrowser.RequestConfig
|
|
77
80
|
import com.margelo.nitro.audiobrowser.ResolvedTrack
|
|
78
81
|
import com.margelo.nitro.audiobrowser.SleepTimer
|
|
82
|
+
import com.margelo.nitro.audiobrowser.TimedMetadata
|
|
79
83
|
import com.margelo.nitro.audiobrowser.Track
|
|
80
|
-
import com.margelo.nitro.audiobrowser.
|
|
84
|
+
import com.margelo.nitro.audiobrowser.TrackMetadata
|
|
81
85
|
import com.margelo.nitro.core.Promise
|
|
82
86
|
import java.util.concurrent.TimeUnit
|
|
87
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
83
88
|
import kotlin.coroutines.resume
|
|
84
89
|
import kotlin.coroutines.resumeWithException
|
|
85
90
|
import kotlin.coroutines.suspendCoroutine
|
|
86
91
|
import kotlinx.coroutines.CancellationException
|
|
87
92
|
import kotlinx.coroutines.Job
|
|
88
93
|
import kotlinx.coroutines.MainScope
|
|
94
|
+
import kotlinx.coroutines.cancel
|
|
89
95
|
import kotlinx.coroutines.cancelChildren
|
|
90
|
-
import kotlinx.coroutines.delay
|
|
91
96
|
import kotlinx.coroutines.launch
|
|
92
97
|
import kotlinx.coroutines.runBlocking
|
|
93
98
|
import timber.log.Timber
|
|
@@ -95,6 +100,58 @@ import timber.log.Timber
|
|
|
95
100
|
@Keep
|
|
96
101
|
@DoNotStrip
|
|
97
102
|
class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Nitro calls this only when JS calls `dispose()` explicitly — never on runtime teardown, which
|
|
106
|
+
* is what [AudioBrowserLifecycleModule] is for. Both routes land here.
|
|
107
|
+
*
|
|
108
|
+
* The Service outlives this object, so everything registered in `init` or on connect has to be
|
|
109
|
+
* undone. Left alone, the running [Player] goes on invoking callbacks into a dead JSI runtime,
|
|
110
|
+
* and every reload strands another ServiceConnection and lifecycle observer, each retaining a
|
|
111
|
+
* BrowserManager.
|
|
112
|
+
*
|
|
113
|
+
* Runs the teardown on main: the state below is written from main-thread coroutines
|
|
114
|
+
* ([onServiceConnected], [onServiceDisconnected]), while dispose itself arrives on the JS thread
|
|
115
|
+
* or the teardown callback's thread.
|
|
116
|
+
*/
|
|
117
|
+
override fun dispose() {
|
|
118
|
+
current.compareAndSet(this, null)
|
|
119
|
+
systemVolumeMonitor.destroy()
|
|
120
|
+
outputMonitor.destroy()
|
|
121
|
+
// Only when still ours: on a reload the replacement may already own the slot.
|
|
122
|
+
carConnectionTarget.compareAndSet(onCarConnectedChanged, null)
|
|
123
|
+
|
|
124
|
+
handler.post {
|
|
125
|
+
connectedService?.let { service ->
|
|
126
|
+
// Identity-guarded: both instances bind to the same singleton Service, so a late dispose
|
|
127
|
+
// must not strip the wiring a replacement has already installed.
|
|
128
|
+
if (service.player.getCallbacks() === callbacks) service.player.setCallbacks(null)
|
|
129
|
+
if (service.player.browser === this) {
|
|
130
|
+
service.player.browser = null
|
|
131
|
+
service.player.forgetBrowserRegistration()
|
|
132
|
+
}
|
|
133
|
+
if (service.onBatteryWarningPendingChanged === onBatteryWarningPending) {
|
|
134
|
+
service.onBatteryWarningPendingChanged = null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
connectedService = null
|
|
138
|
+
|
|
139
|
+
// Dropping the reference disconnects nothing — the controller stays in the session's
|
|
140
|
+
// connected list, holding a binder and a listener.
|
|
141
|
+
mediaBrowserFuture?.let { MediaBrowser.releaseFuture(it) }
|
|
142
|
+
mediaBrowserFuture = null
|
|
143
|
+
setupPromise = null
|
|
144
|
+
|
|
145
|
+
serviceBinding.unbind(this)
|
|
146
|
+
|
|
147
|
+
ProcessLifecycleOwner.get().lifecycle.removeObserver(lifecycleObserver)
|
|
148
|
+
// After the observer removal, so this post is not the one dropped.
|
|
149
|
+
handler.removeCallbacksAndMessages(null)
|
|
150
|
+
mainScope.cancel()
|
|
151
|
+
}
|
|
152
|
+
super.dispose()
|
|
153
|
+
}
|
|
154
|
+
|
|
98
155
|
private val mainScope = MainScope()
|
|
99
156
|
private var navigationJob: Job? = null
|
|
100
157
|
private val handler = Handler(Looper.getMainLooper())
|
|
@@ -107,14 +164,18 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
107
164
|
NativeBrowserConfiguration(
|
|
108
165
|
path = null,
|
|
109
166
|
request = null,
|
|
167
|
+
requestResolver = null,
|
|
168
|
+
browse = null,
|
|
169
|
+
browseResolver = null,
|
|
110
170
|
media = null,
|
|
111
171
|
artwork = null,
|
|
172
|
+
nowPlayingArtwork = null,
|
|
112
173
|
routes = null,
|
|
113
174
|
singleTrack = null,
|
|
114
175
|
handleTrackLoad = null,
|
|
115
176
|
androidControllerOfflineError = null,
|
|
116
|
-
|
|
117
|
-
|
|
177
|
+
carPlayLoadingTitle = null,
|
|
178
|
+
resolveAlbumUrl = null,
|
|
118
179
|
formatNavigationError = null,
|
|
119
180
|
)
|
|
120
181
|
|
|
@@ -123,17 +184,31 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
123
184
|
setOnPathChanged { path -> onPathChanged(path) }
|
|
124
185
|
setOnContentChanged { content -> onContentChanged(content) }
|
|
125
186
|
setOnTabsChanged { tabs -> onTabsChanged(tabs) }
|
|
187
|
+
setOnArtworkRegistriesCleared { connectedService?.player?.browseArtworkRegistry?.clear() }
|
|
126
188
|
}
|
|
127
189
|
|
|
128
190
|
private val systemVolumeMonitor = SystemVolumeMonitor(context)
|
|
191
|
+
private val outputMonitor = OutputMonitor(context)
|
|
129
192
|
|
|
130
193
|
// MARK: Player state
|
|
131
194
|
private var updateOptions: PlayerUpdateOptions = PlayerUpdateOptions()
|
|
132
195
|
private var mediaBrowserFuture: ListenableFuture<MediaBrowser>? = null
|
|
133
196
|
private var setupOptions = PlayerSetupOptions()
|
|
134
197
|
private var connectedService: Service? = null
|
|
198
|
+
|
|
199
|
+
private val serviceBinding = ServiceBinding(context)
|
|
200
|
+
|
|
201
|
+
/** Held rather than inlined so [dispose] can tell its own wiring from a replacement's. */
|
|
202
|
+
private val onBatteryWarningPending: (Boolean) -> Unit = { pending ->
|
|
203
|
+
post { onBatteryWarningPendingChanged(BatteryWarningPendingChangedEvent(pending)) }
|
|
204
|
+
}
|
|
135
205
|
private var setupPromise: ((Unit) -> Unit)? = null
|
|
136
206
|
|
|
207
|
+
// Initial player state staged before the player exists — from setup options or the imperative
|
|
208
|
+
// setters called pre-bind. Strict last-write-wins; consumed when the player comes up.
|
|
209
|
+
private var pendingPlayWhenReady: Boolean? = null
|
|
210
|
+
private var pendingRepeatMode: RepeatMode? = null
|
|
211
|
+
|
|
137
212
|
/** Post callback to main handler for consistent async delivery to JS - avoids deadlocks */
|
|
138
213
|
private fun post(block: () -> Unit) = handler.post(block)
|
|
139
214
|
|
|
@@ -144,13 +219,21 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
144
219
|
override var onNavigationError: (NavigationErrorEvent) -> Unit = {}
|
|
145
220
|
override var onFormattedNavigationError: (FormattedNavigationError?) -> Unit = {}
|
|
146
221
|
|
|
222
|
+
// MARK: Gate callbacks (native→JS; set from JS, native CALLS them)
|
|
223
|
+
// Default resolver: only reachable in the init window where the native side has recorded
|
|
224
|
+
// hasResolver=true but JS hasn't yet assigned the real `resolveGate` slot. It DENIES by default
|
|
225
|
+
// (gated=true) so an active gate never serves content during that window — same fail-closed
|
|
226
|
+
// direction as the resolver-error path in [gateDecision]. The static-gate fast path skips it
|
|
227
|
+
// entirely once a resolver-less gate is set.
|
|
228
|
+
override var resolveGate: (request: NativeGateRequest) -> Promise<Promise<GateDecision>> = {
|
|
229
|
+
Promise.resolved(Promise.resolved(GateDecision(gated = true, gate = null)))
|
|
230
|
+
}
|
|
231
|
+
override var onGate: (event: GateEvent) -> Unit = {}
|
|
232
|
+
|
|
147
233
|
// MARK: Player callbacks
|
|
148
234
|
override var onPlaybackChanged: (data: Playback) -> Unit = {}
|
|
149
|
-
override var onRemoteBookmark: () -> Unit = {}
|
|
150
|
-
override var onRemoteDislike: () -> Unit = {}
|
|
151
235
|
override var onRemoteJumpBackward: (RemoteJumpBackwardEvent) -> Unit = {}
|
|
152
236
|
override var onRemoteJumpForward: (RemoteJumpForwardEvent) -> Unit = {}
|
|
153
|
-
override var onRemoteLike: () -> Unit = {}
|
|
154
237
|
override var onRemoteNext: () -> Unit = {}
|
|
155
238
|
override var onRemotePause: () -> Unit = {}
|
|
156
239
|
override var onChapterMetadata: (chapters: Array<ChapterMetadata>) -> Unit = {}
|
|
@@ -162,6 +245,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
162
245
|
{}
|
|
163
246
|
override var onPlaybackPlayingState: (data: PlayingState) -> Unit = {}
|
|
164
247
|
override var onPlaybackProgressUpdated: (data: PlaybackProgressUpdatedEvent) -> Unit = {}
|
|
248
|
+
override var onPlaybackInterval: () -> Unit = {}
|
|
165
249
|
override var onPlaybackQueueEnded: (data: PlaybackQueueEndedEvent) -> Unit = {}
|
|
166
250
|
override var onPlaybackQueueChanged: (queue: Array<Track>) -> Unit = {}
|
|
167
251
|
override var onPlaybackRepeatModeChanged: (data: RepeatModeChangedEvent) -> Unit = {}
|
|
@@ -171,7 +255,6 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
171
255
|
override var onRemotePlaySearch: (RemotePlaySearchEvent) -> Unit = {}
|
|
172
256
|
override var onRemotePrevious: () -> Unit = {}
|
|
173
257
|
override var onRemoteSeek: (RemoteSeekEvent) -> Unit = {}
|
|
174
|
-
override var onRemoteSetRating: (RemoteSetRatingEvent) -> Unit = {}
|
|
175
258
|
override var onRemoteSkip: (RemoteSkipEvent) -> Unit = {}
|
|
176
259
|
override var onRemoteStop: () -> Unit = {}
|
|
177
260
|
override var onOptionsChanged: (Options) -> Unit = {}
|
|
@@ -184,14 +267,11 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
184
267
|
override var onBatteryOptimizationStatusChanged: (BatteryOptimizationStatusChangedEvent) -> Unit =
|
|
185
268
|
{}
|
|
186
269
|
override var onSystemVolumeChanged: (Double) -> Unit = {}
|
|
187
|
-
override var
|
|
270
|
+
override var onOutputChanged: (Output) -> Unit = {}
|
|
188
271
|
|
|
189
272
|
// MARK: Remote handlers
|
|
190
|
-
override var handleRemoteBookmark: (() -> Unit)? = null
|
|
191
|
-
override var handleRemoteDislike: (() -> Unit)? = null
|
|
192
273
|
override var handleRemoteJumpBackward: ((RemoteJumpBackwardEvent) -> Unit)? = null
|
|
193
274
|
override var handleRemoteJumpForward: ((RemoteJumpForwardEvent) -> Unit)? = null
|
|
194
|
-
override var handleRemoteLike: (() -> Unit)? = null
|
|
195
275
|
override var handleRemoteNext: (() -> Unit)? = null
|
|
196
276
|
override var handleRemotePause: (() -> Unit)? = null
|
|
197
277
|
override var handleRemotePlay: (() -> Unit)? = null
|
|
@@ -211,12 +291,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
211
291
|
}
|
|
212
292
|
|
|
213
293
|
init {
|
|
294
|
+
current.set(this)
|
|
295
|
+
|
|
214
296
|
// Auto-bind to service if it's already running
|
|
215
297
|
launchInScope {
|
|
216
298
|
try {
|
|
217
299
|
Timber.d("Attempting to auto-bind to existing AudioBrowserService from AudioBrowser")
|
|
218
300
|
val intent = Intent(context, Service::class.java)
|
|
219
|
-
val bound =
|
|
301
|
+
val bound = serviceBinding.bind(intent, this@AudioBrowser)
|
|
220
302
|
Timber.d("Auto-bind result: $bound")
|
|
221
303
|
|
|
222
304
|
if (!bound) {
|
|
@@ -232,9 +314,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
232
314
|
handler.post { ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleObserver) }
|
|
233
315
|
|
|
234
316
|
// Observe system volume changes
|
|
235
|
-
systemVolumeMonitor.setOnVolumeChanged { volume ->
|
|
236
|
-
|
|
237
|
-
|
|
317
|
+
systemVolumeMonitor.setOnVolumeChanged { volume -> post { onSystemVolumeChanged(volume) } }
|
|
318
|
+
|
|
319
|
+
// Observe current audio output changes (active media route via AudioManager)
|
|
320
|
+
outputMonitor.setOnOutputChanged { output -> post { onOutputChanged(output) } }
|
|
321
|
+
outputMonitor.start()
|
|
322
|
+
|
|
323
|
+
// Observe the car connection (Android Auto / Android Automotive)
|
|
324
|
+
startCarConnectionObserver(context, handler)
|
|
238
325
|
}
|
|
239
326
|
|
|
240
327
|
// ============================================================================
|
|
@@ -244,8 +331,12 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
244
331
|
internal fun buildConfig(): BrowserConfig {
|
|
245
332
|
return BrowserConfig(
|
|
246
333
|
request = _configuration.request,
|
|
334
|
+
requestResolver = _configuration.requestResolver,
|
|
335
|
+
browse = _configuration.browse,
|
|
336
|
+
browseResolver = _configuration.browseResolver,
|
|
247
337
|
media = _configuration.media,
|
|
248
338
|
artwork = _configuration.artwork,
|
|
339
|
+
nowPlayingArtwork = _configuration.nowPlayingArtwork,
|
|
249
340
|
routes = _configuration.routes,
|
|
250
341
|
singleTrack = _configuration.singleTrack ?: false,
|
|
251
342
|
androidControllerOfflineError = _configuration.androidControllerOfflineError ?: true,
|
|
@@ -253,34 +344,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
253
344
|
}
|
|
254
345
|
|
|
255
346
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
val artworkConfig = _configuration.artwork ?: return null
|
|
261
|
-
val baseConfig =
|
|
262
|
-
_configuration.request?.let { RequestConfigBuilder.toRequestConfig(it) }
|
|
263
|
-
?: RequestConfig(null, null, null, null, null, null, null, null)
|
|
264
|
-
return CoilBitmapLoader.ArtworkConfig(baseConfig, artworkConfig)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Creates a request config for media URL transformation by merging base and media configs.
|
|
269
|
-
* Returns null if no media configuration is set.
|
|
347
|
+
* Media URL transformation for [com.audiobrowser.player.TransformingDataSource]. Resolution lives
|
|
348
|
+
* in [resolveMediaUrl] (browser/BrowserUrlResolution.kt); this shell owns only the blocking
|
|
349
|
+
* bridge: it runs on ExoPlayer's IO thread (TransformingDataSource.open), so blocking here is
|
|
350
|
+
* safe and intentional.
|
|
270
351
|
*/
|
|
271
352
|
fun getMediaRequestConfig(originalUrl: String): MediaRequestConfig? {
|
|
272
|
-
val mediaConfig = _configuration.media ?: return null
|
|
273
|
-
|
|
274
353
|
return try {
|
|
275
|
-
|
|
276
|
-
val baseConfig =
|
|
277
|
-
_configuration.request?.let { RequestConfigBuilder.toRequestConfig(it) }
|
|
278
|
-
?: RequestConfig(null, null, null, null, null, null, null, null)
|
|
279
|
-
val urlRequestConfig = RequestConfig(null, originalUrl, null, null, null, null, null, null)
|
|
280
|
-
val mergedBaseConfig = RequestConfigBuilder.mergeConfig(baseConfig, urlRequestConfig)
|
|
281
|
-
|
|
282
|
-
// Apply media transformation
|
|
283
|
-
runBlocking { RequestConfigBuilder.mergeConfig(mergedBaseConfig, mediaConfig) }
|
|
354
|
+
runBlocking { browserManager.resolveMediaUrl(originalUrl) }
|
|
284
355
|
} catch (e: Exception) {
|
|
285
356
|
Timber.e(e, "Failed to transform media URL: $originalUrl")
|
|
286
357
|
null
|
|
@@ -292,10 +363,15 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
292
363
|
return _configuration.routes?.any { it.path != BrowserManager.SEARCH_ROUTE_PATH } == true
|
|
293
364
|
}
|
|
294
365
|
|
|
295
|
-
|
|
366
|
+
// Suspending on purpose: queryTabs() resolves tabs by invoking JS callbacks,
|
|
367
|
+
// which can only run on the JS thread. Callers MUST invoke this from a
|
|
368
|
+
// coroutine (off the JS thread) — never via runBlocking on a synchronous Nitro
|
|
369
|
+
// setter, which runs on the JS thread and would deadlock (the JS thread blocks
|
|
370
|
+
// waiting for callbacks that need that same thread to run).
|
|
371
|
+
private suspend fun getDefaultPath(): String? {
|
|
296
372
|
return try {
|
|
297
373
|
browserManager.config = buildConfig()
|
|
298
|
-
val tabs =
|
|
374
|
+
val tabs = browserManager.queryTabs()
|
|
299
375
|
if (tabs.isNotEmpty()) {
|
|
300
376
|
Timber.d("Using first tab as default path: ${tabs[0].url}")
|
|
301
377
|
tabs[0].url
|
|
@@ -303,6 +379,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
303
379
|
Timber.d("Using root path as default: /")
|
|
304
380
|
"/"
|
|
305
381
|
}
|
|
382
|
+
} catch (e: CancellationException) {
|
|
383
|
+
throw e // Rethrow so cooperative cancellation isn't swallowed
|
|
306
384
|
} catch (e: Exception) {
|
|
307
385
|
Timber.e(e, "Failed to get default path, falling back to /")
|
|
308
386
|
"/"
|
|
@@ -318,23 +396,25 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
318
396
|
set(value) {
|
|
319
397
|
if (hasValidConfiguration()) {
|
|
320
398
|
browserManager.config = buildConfig()
|
|
321
|
-
(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
399
|
+
clearNavigationError()
|
|
400
|
+
|
|
401
|
+
// Cancel previous navigation to avoid race conditions
|
|
402
|
+
navigationJob?.cancel()
|
|
403
|
+
|
|
404
|
+
// Resolve the default path inside the coroutine (getDefaultPath queries
|
|
405
|
+
// tabs via JS callbacks) so it never blocks the JS thread this setter
|
|
406
|
+
// runs on.
|
|
407
|
+
navigationJob =
|
|
408
|
+
mainScope.launch {
|
|
409
|
+
val path = value ?: getDefaultPath() ?: return@launch
|
|
410
|
+
try {
|
|
411
|
+
browserManager.navigate(path)
|
|
412
|
+
} catch (e: CancellationException) {
|
|
413
|
+
throw e // Rethrow to properly cancel
|
|
414
|
+
} catch (e: Exception) {
|
|
415
|
+
handleBrowserException(e, path, "setting path: $path")
|
|
336
416
|
}
|
|
337
|
-
|
|
417
|
+
}
|
|
338
418
|
}
|
|
339
419
|
}
|
|
340
420
|
|
|
@@ -352,45 +432,54 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
352
432
|
// This allows Android Auto to start browsing content
|
|
353
433
|
connectedService?.player?.notifyBrowserConfigurationReady()
|
|
354
434
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
435
|
+
clearNavigationError()
|
|
436
|
+
|
|
437
|
+
// Cancel previous navigation to avoid race conditions
|
|
438
|
+
navigationJob?.cancel()
|
|
439
|
+
|
|
440
|
+
// Navigate to the initial path, or default to the first tab. The default
|
|
441
|
+
// path is resolved inside the coroutine (getDefaultPath queries tabs via
|
|
442
|
+
// JS callbacks) so it never blocks the JS thread this setter runs on —
|
|
443
|
+
// doing it synchronously here via runBlocking deadlocked: the JS callbacks
|
|
444
|
+
// can't run because the JS thread is blocked waiting for them.
|
|
445
|
+
navigationJob =
|
|
446
|
+
mainScope.launch {
|
|
447
|
+
val path = value.path ?: getDefaultPath() ?: return@launch
|
|
448
|
+
try {
|
|
449
|
+
browserManager.navigate(path)
|
|
450
|
+
} catch (e: CancellationException) {
|
|
451
|
+
throw e // Rethrow to properly cancel
|
|
452
|
+
} catch (e: Exception) {
|
|
453
|
+
handleBrowserException(e, path, "setting configuration path: $path")
|
|
371
454
|
}
|
|
372
|
-
|
|
455
|
+
}
|
|
373
456
|
}
|
|
374
457
|
|
|
375
458
|
private var navigationError: NavigationError? = null
|
|
376
459
|
private var formattedNavigationError: FormattedNavigationError? = null
|
|
377
460
|
|
|
378
461
|
override fun getNavigationError(): NavigationError? = navigationError
|
|
462
|
+
|
|
379
463
|
override fun getFormattedNavigationError(): FormattedNavigationError? = formattedNavigationError
|
|
380
464
|
|
|
381
465
|
/** Creates a default formatted error from a NavigationError */
|
|
382
466
|
private fun defaultFormattedError(error: NavigationError): FormattedNavigationError {
|
|
383
|
-
val title =
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
467
|
+
val title =
|
|
468
|
+
when (error.code) {
|
|
469
|
+
NavigationErrorType.CONTENT_NOT_FOUND -> "Content Not Found"
|
|
470
|
+
NavigationErrorType.NETWORK_ERROR -> "Network Error"
|
|
471
|
+
NavigationErrorType.HTTP_ERROR -> {
|
|
472
|
+
// Use system-localized HTTP status text (e.g., "Not Found", "Service Unavailable")
|
|
473
|
+
error.statusCode?.let { httpStatusText(it.toInt()) } ?: "Server Error"
|
|
474
|
+
}
|
|
475
|
+
NavigationErrorType.CALLBACK_ERROR -> "Error"
|
|
476
|
+
NavigationErrorType.UNKNOWN_ERROR -> "Error"
|
|
477
|
+
// Not a failure — a container that resolved with no children. Neutral copy. See ADR 0001.
|
|
478
|
+
NavigationErrorType.EMPTY_CONTENT -> "Nothing here"
|
|
479
|
+
NavigationErrorType.TIMEOUT -> "Couldn't load"
|
|
389
480
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
return FormattedNavigationError(title, error.message)
|
|
481
|
+
// Omit an empty message so it renders as title-only (e.g. the empty-content case).
|
|
482
|
+
return FormattedNavigationError(title, error.message.takeIf { it.isNotEmpty() })
|
|
394
483
|
}
|
|
395
484
|
|
|
396
485
|
/** Returns localized HTTP status text for the given status code */
|
|
@@ -457,19 +546,35 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
457
546
|
}
|
|
458
547
|
is NetworkException -> {
|
|
459
548
|
Timber.e(e, "Network error $logContext")
|
|
460
|
-
setNavigationError(
|
|
549
|
+
setNavigationError(
|
|
550
|
+
NavigationErrorType.NETWORK_ERROR,
|
|
551
|
+
e.message ?: "Network request failed",
|
|
552
|
+
path,
|
|
553
|
+
)
|
|
461
554
|
}
|
|
462
555
|
is ContentNotFoundException -> {
|
|
463
556
|
Timber.e(e, "Content not found $logContext")
|
|
464
|
-
setNavigationError(
|
|
557
|
+
setNavigationError(
|
|
558
|
+
NavigationErrorType.CONTENT_NOT_FOUND,
|
|
559
|
+
e.message ?: "Content not found",
|
|
560
|
+
path,
|
|
561
|
+
)
|
|
465
562
|
}
|
|
466
563
|
is CallbackException -> {
|
|
467
564
|
Timber.e(e, "Callback error $logContext")
|
|
468
|
-
setNavigationError(
|
|
565
|
+
setNavigationError(
|
|
566
|
+
NavigationErrorType.CALLBACK_ERROR,
|
|
567
|
+
e.message ?: "An error occurred",
|
|
568
|
+
path,
|
|
569
|
+
)
|
|
469
570
|
}
|
|
470
571
|
else -> {
|
|
471
572
|
Timber.e(e, "Unexpected error $logContext")
|
|
472
|
-
setNavigationError(
|
|
573
|
+
setNavigationError(
|
|
574
|
+
NavigationErrorType.UNKNOWN_ERROR,
|
|
575
|
+
e.message ?: "An unexpected error occurred",
|
|
576
|
+
path,
|
|
577
|
+
)
|
|
473
578
|
}
|
|
474
579
|
}
|
|
475
580
|
}
|
|
@@ -529,10 +634,17 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
529
634
|
val index = player.tracks.indexOfFirst { it.src == trackId }
|
|
530
635
|
if (index >= 0) {
|
|
531
636
|
Timber.d("Queue already from $parentPath, skipping to index $index")
|
|
532
|
-
handleTrackLoad(
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
637
|
+
handleTrackLoad(
|
|
638
|
+
_configuration.handleTrackLoad,
|
|
639
|
+
track,
|
|
640
|
+
player.tracks,
|
|
641
|
+
index.toDouble(),
|
|
642
|
+
intercepted = {},
|
|
643
|
+
defaultBehavior = {
|
|
644
|
+
player.skipTo(index)
|
|
645
|
+
player.play()
|
|
646
|
+
},
|
|
647
|
+
)
|
|
536
648
|
return@launch
|
|
537
649
|
}
|
|
538
650
|
}
|
|
@@ -545,17 +657,31 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
545
657
|
Timber.d(
|
|
546
658
|
"Loading expanded queue: ${tracks.size} tracks, starting at index $startIndex"
|
|
547
659
|
)
|
|
548
|
-
handleTrackLoad(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
660
|
+
handleTrackLoad(
|
|
661
|
+
_configuration.handleTrackLoad,
|
|
662
|
+
track,
|
|
663
|
+
tracks,
|
|
664
|
+
startIndex.toDouble(),
|
|
665
|
+
intercepted = {},
|
|
666
|
+
defaultBehavior = {
|
|
667
|
+
// Replace queue and seek to selected track
|
|
668
|
+
// Use internal player methods directly to avoid blocking on main thread
|
|
669
|
+
player.setQueue(tracks, startIndex, sourcePath = parentPath)
|
|
670
|
+
player.play()
|
|
671
|
+
},
|
|
672
|
+
)
|
|
554
673
|
return@launch
|
|
555
674
|
} else {
|
|
556
675
|
// Fallback: just load the single track
|
|
557
676
|
Timber.w("Queue expansion failed, loading single track")
|
|
558
|
-
handleTrackLoad(
|
|
677
|
+
handleTrackLoad(
|
|
678
|
+
_configuration.handleTrackLoad,
|
|
679
|
+
track,
|
|
680
|
+
arrayOf(track),
|
|
681
|
+
0.0,
|
|
682
|
+
intercepted = {},
|
|
683
|
+
defaultBehavior = { player.load(track) },
|
|
684
|
+
)
|
|
559
685
|
}
|
|
560
686
|
}
|
|
561
687
|
// Navigate to browsable track to show browsing UI
|
|
@@ -566,7 +692,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
566
692
|
// If track is playable (has src), load it into player
|
|
567
693
|
track.src != null -> {
|
|
568
694
|
Timber.d("Loading playable track into player: ${track.title}")
|
|
569
|
-
handleTrackLoad(
|
|
695
|
+
handleTrackLoad(
|
|
696
|
+
_configuration.handleTrackLoad,
|
|
697
|
+
track,
|
|
698
|
+
arrayOf(track),
|
|
699
|
+
0.0,
|
|
700
|
+
intercepted = {},
|
|
701
|
+
defaultBehavior = { player.load(track) },
|
|
702
|
+
)
|
|
570
703
|
}
|
|
571
704
|
else -> {
|
|
572
705
|
throw IllegalArgumentException("Track must have either an 'url' or an 'src' property")
|
|
@@ -580,7 +713,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
580
713
|
}
|
|
581
714
|
}
|
|
582
715
|
|
|
583
|
-
override fun
|
|
716
|
+
override fun search(query: String): Promise<Array<Track>> {
|
|
584
717
|
return Promise.async(mainScope) {
|
|
585
718
|
Timber.d("Searching for: $query")
|
|
586
719
|
val searchResults = browserManager.search(query)
|
|
@@ -607,32 +740,132 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
607
740
|
}
|
|
608
741
|
}
|
|
609
742
|
|
|
743
|
+
override fun invalidateAllContent() {
|
|
744
|
+
Timber.d("Invalidating all content")
|
|
745
|
+
|
|
746
|
+
// Clear all cached content so every path re-fetches fresh data
|
|
747
|
+
browserManager.clearContentCache()
|
|
748
|
+
|
|
749
|
+
// Notify external media controllers (Android Auto) to refresh subscribed paths
|
|
750
|
+
connectedService?.player?.invalidateAllContent()
|
|
751
|
+
|
|
752
|
+
// Refresh whatever the browser is currently viewing
|
|
753
|
+
mainScope.launch { browserManager.refresh() }
|
|
754
|
+
}
|
|
755
|
+
|
|
610
756
|
override fun setFavorites(favorites: Array<String>) {
|
|
611
757
|
browserManager.setFavorites(favorites.toList())
|
|
612
758
|
}
|
|
613
759
|
|
|
760
|
+
// MARK: Gate
|
|
761
|
+
|
|
762
|
+
/** A gate decision for one request: whether to gate, and which chrome to render. */
|
|
763
|
+
data class GateOutcome(val gated: Boolean, val chrome: Gate?)
|
|
764
|
+
|
|
765
|
+
// The minimal chrome rendered when a gate is active but no override or stored
|
|
766
|
+
// default chrome exists (the resolver-only setGate overload).
|
|
767
|
+
private val builtInGate = Gate("Unavailable", null)
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* The three gate fields as one immutable triple so [gateDecision] reads them as a unit. Written
|
|
771
|
+
* from the JS thread, read from the Media3 application thread (the enforcement sites in
|
|
772
|
+
* MediaSessionCallback consult it) — hence the single field is @Volatile.
|
|
773
|
+
*
|
|
774
|
+
* Folding the three former @Volatile fields into one reference is what makes the read atomic:
|
|
775
|
+
*
|
|
776
|
+
* @Volatile gives per-field visibility but NO atomicity across a compound read, so reading three
|
|
777
|
+
* separate volatiles in [gateDecision] could interleave with a concurrent [clearGate] and yield
|
|
778
|
+
* a torn `(active=true, chrome=null)` state that crashed the force-unwrap at the serve sites.
|
|
779
|
+
* One volatile reference read once cannot tear.
|
|
780
|
+
*/
|
|
781
|
+
private data class GateState(val active: Boolean, val chrome: Gate?, val hasResolver: Boolean)
|
|
782
|
+
|
|
783
|
+
@Volatile private var gateState = GateState(active = false, chrome = null, hasResolver = false)
|
|
784
|
+
|
|
785
|
+
override fun setGate(gate: Gate?, hasResolver: Boolean) {
|
|
786
|
+
// Single atomic assignment — readers never see a half-updated triple.
|
|
787
|
+
gateState = GateState(active = true, chrome = gate, hasResolver = hasResolver)
|
|
788
|
+
// Re-query every subscribed parent so a connected controller (Android
|
|
789
|
+
// Auto) swaps its lists for the gate tile without reconnecting. Notify
|
|
790
|
+
// only — the content cache stays warm for when the gate clears.
|
|
791
|
+
connectedService?.player?.invalidateAllContent()
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
override fun clearGate() {
|
|
795
|
+
if (!gateState.active) return
|
|
796
|
+
// Single atomic assignment — readers never see a half-cleared triple.
|
|
797
|
+
gateState = GateState(active = false, chrome = null, hasResolver = false)
|
|
798
|
+
connectedService?.player?.invalidateAllContent()
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* The single gate choke point. Each enforcement site (browse / search) calls this for the current
|
|
803
|
+
* request and serves the gate chrome when [GateOutcome.gated] is true. Structurally similar to
|
|
804
|
+
* the iOS `gateDecision(for:)` helper, but note the concurrency model differs: iOS confines gate
|
|
805
|
+
* state to `@MainActor`; here the consistent read comes from snapshotting the single [gateState]
|
|
806
|
+
* reference once (see [GateState]).
|
|
807
|
+
* - No active gate → allow.
|
|
808
|
+
* - Active gate, no resolver → static fast path: gate with the stored default chrome (or built-in
|
|
809
|
+
* if none), no JS hop.
|
|
810
|
+
* - Active gate with a resolver → ask JS per request. A resolver that throws / rejects / times
|
|
811
|
+
* out FAILS CLOSED by design: while a gate is active the consumer has declared content blocked,
|
|
812
|
+
* so the only safe fallback when we cannot compute the per-request decision is the gate's own
|
|
813
|
+
* chrome — never the content. A *successful* `gated:false` still allows. Chrome order on a
|
|
814
|
+
* gated decision: override → stored default → built-in.
|
|
815
|
+
*
|
|
816
|
+
* Every gated return guards chrome against null (`?: builtInGate`) so a gated outcome always
|
|
817
|
+
* carries a renderable chrome — the serve sites force-unwrap it.
|
|
818
|
+
*/
|
|
819
|
+
suspend fun gateDecision(request: NativeGateRequest): GateOutcome {
|
|
820
|
+
// Read the whole gate triple once; a single volatile reference can't tear (see [GateState]).
|
|
821
|
+
val s = gateState
|
|
822
|
+
if (!s.active) return GateOutcome(false, null)
|
|
823
|
+
if (!s.hasResolver) return GateOutcome(true, s.chrome ?: builtInGate) // static fast path
|
|
824
|
+
val decision =
|
|
825
|
+
runCatching { resolveGate(request).await().await() }.getOrNull()
|
|
826
|
+
?: return GateOutcome(true, s.chrome ?: builtInGate) // fail CLOSED on resolver error
|
|
827
|
+
if (!decision.gated) return GateOutcome(false, null) // explicit allow
|
|
828
|
+
return GateOutcome(true, decision.gate ?: s.chrome ?: builtInGate)
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// MARK: Car connection (Android Auto / Android Automotive)
|
|
832
|
+
|
|
833
|
+
override fun isCarConnected(): Boolean = carConnected
|
|
834
|
+
|
|
835
|
+
override var onCarConnectedChanged: (Boolean) -> Unit = {}
|
|
836
|
+
set(value) {
|
|
837
|
+
field = value
|
|
838
|
+
carConnectionTarget.set(value)
|
|
839
|
+
// Immediately notify current state (the car may have connected before
|
|
840
|
+
// this JS runtime subscribed) — mirrors the iOS behavior.
|
|
841
|
+
value(carConnected)
|
|
842
|
+
}
|
|
843
|
+
|
|
614
844
|
// ============================================================================
|
|
615
845
|
// MARK: Player Setup and Options
|
|
616
846
|
// ============================================================================
|
|
617
847
|
|
|
618
|
-
override fun setupPlayer(options:
|
|
848
|
+
override fun setupPlayer(options: NativeSetupPlayerOptions): Promise<Unit> {
|
|
619
849
|
return Promise.async(mainScope) {
|
|
620
850
|
setupOptions.update(options)
|
|
851
|
+
// The bundled runtime options and initial state are part of the atomic launch
|
|
852
|
+
// description: stage them here, apply them together with the engine below (or at
|
|
853
|
+
// service connect). Last-write-wins with their imperative counterparts.
|
|
854
|
+
options.options?.let { updateOptions.updateFromBridge(it) }
|
|
855
|
+
options.repeatMode?.let { pendingRepeatMode = it }
|
|
856
|
+
options.playWhenReady?.let { pendingPlayWhenReady = it }
|
|
621
857
|
|
|
622
858
|
connectedService?.let {
|
|
859
|
+
it.player.applyOptions(updateOptions)
|
|
623
860
|
it.player.setup(setupOptions)
|
|
861
|
+
applyPendingPlayerState(it.player)
|
|
624
862
|
return@async
|
|
625
863
|
}
|
|
626
864
|
|
|
627
865
|
// Service not connected yet, bind to service
|
|
628
866
|
suspendCoroutine<Unit> { continuation ->
|
|
629
867
|
Timber.d("Binding to AudioBrowserService")
|
|
630
|
-
val bound =
|
|
631
|
-
context.bindService(
|
|
632
|
-
Intent(context, Service::class.java),
|
|
633
|
-
this@AudioBrowser,
|
|
634
|
-
Context.BIND_AUTO_CREATE,
|
|
635
|
-
)
|
|
868
|
+
val bound = serviceBinding.bind(Intent(context, Service::class.java), this@AudioBrowser)
|
|
636
869
|
|
|
637
870
|
if (!bound) {
|
|
638
871
|
continuation.resumeWithException(
|
|
@@ -652,8 +885,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
652
885
|
connectedService?.let { player.applyOptions(updateOptions) }
|
|
653
886
|
}
|
|
654
887
|
|
|
655
|
-
override fun getOptions():
|
|
656
|
-
|
|
888
|
+
override fun getOptions(): Options {
|
|
889
|
+
// The holder is the source of truth whether or not the player exists yet — updateOptions
|
|
890
|
+
// merges into it and setup/connect applies it to the player.
|
|
891
|
+
return updateOptions.toNitro()
|
|
657
892
|
}
|
|
658
893
|
|
|
659
894
|
// ============================================================================
|
|
@@ -666,8 +901,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
666
901
|
|
|
667
902
|
override fun reset() = runBlockingOnMain {
|
|
668
903
|
player.stop()
|
|
669
|
-
delay(300) // Allow playback to stop
|
|
670
904
|
player.clear()
|
|
905
|
+
// clear() while IDLE emits no engine event, so the state machine never
|
|
906
|
+
// leaves STOPPED — a reset queue is "nothing loaded".
|
|
907
|
+
player.setPlaybackState(PlaybackState.NONE)
|
|
671
908
|
}
|
|
672
909
|
|
|
673
910
|
override fun play() = runBlockingOnMain { player.play() }
|
|
@@ -679,10 +916,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
679
916
|
override fun stop() = runBlockingOnMain { player.stop() }
|
|
680
917
|
|
|
681
918
|
override fun setPlayWhenReady(playWhenReady: Boolean) = runBlockingOnMain {
|
|
682
|
-
player
|
|
919
|
+
// Pre-setup this stages the intent for the player to come up with — never throws.
|
|
920
|
+
connectedService?.player?.let { it.playWhenReady = playWhenReady }
|
|
921
|
+
?: run { pendingPlayWhenReady = playWhenReady }
|
|
683
922
|
}
|
|
684
923
|
|
|
685
|
-
override fun getPlayWhenReady(): Boolean = runBlockingOnMain {
|
|
924
|
+
override fun getPlayWhenReady(): Boolean = runBlockingOnMain {
|
|
925
|
+
connectedService?.player?.playWhenReady ?: pendingPlayWhenReady ?: false
|
|
926
|
+
}
|
|
686
927
|
|
|
687
928
|
override fun seekTo(position: Double) = runBlockingOnMain {
|
|
688
929
|
player.seekTo((position * 1000).toLong(), TimeUnit.MILLISECONDS)
|
|
@@ -692,6 +933,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
692
933
|
player.seekBy((offset * 1000).toLong(), TimeUnit.MILLISECONDS)
|
|
693
934
|
}
|
|
694
935
|
|
|
936
|
+
override fun seekToLiveEdge() = runBlockingOnMain { player.seekToLiveEdge() }
|
|
937
|
+
|
|
695
938
|
override fun setVolume(level: Double) = runBlockingOnMain { player.volume = level.toFloat() }
|
|
696
939
|
|
|
697
940
|
override fun getVolume(): Double = runBlockingOnMain { player.volume.toDouble() }
|
|
@@ -712,9 +955,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
712
955
|
|
|
713
956
|
override fun getPlayingState(): PlayingState = runBlockingOnMain { player.getPlayingState() }
|
|
714
957
|
|
|
715
|
-
override fun getRepeatMode(): RepeatMode = runBlockingOnMain {
|
|
958
|
+
override fun getRepeatMode(): RepeatMode = runBlockingOnMain {
|
|
959
|
+
connectedService?.player?.repeatMode ?: pendingRepeatMode ?: RepeatMode.OFF
|
|
960
|
+
}
|
|
716
961
|
|
|
717
|
-
override fun setRepeatMode(mode: RepeatMode) = runBlockingOnMain {
|
|
962
|
+
override fun setRepeatMode(mode: RepeatMode) = runBlockingOnMain {
|
|
963
|
+
// Pre-setup this stages the mode for the player to come up with — never throws.
|
|
964
|
+
connectedService?.player?.let { it.repeatMode = mode } ?: run { pendingRepeatMode = mode }
|
|
965
|
+
}
|
|
718
966
|
|
|
719
967
|
override fun getShuffleEnabled(): Boolean = runBlockingOnMain { player.shuffleMode }
|
|
720
968
|
|
|
@@ -722,6 +970,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
722
970
|
player.shuffleMode = enabled
|
|
723
971
|
}
|
|
724
972
|
|
|
973
|
+
override fun setPlaybackIntervalEnabled(enabled: Boolean) = runBlockingOnMain {
|
|
974
|
+
player.setPlaybackIntervalEnabled(enabled)
|
|
975
|
+
}
|
|
976
|
+
|
|
725
977
|
override fun getPlaybackError(): PlaybackError? = runBlockingOnMain { player.playbackError }
|
|
726
978
|
|
|
727
979
|
override fun retry() = runBlockingOnMain { player.prepare() }
|
|
@@ -770,9 +1022,9 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
770
1022
|
}
|
|
771
1023
|
}
|
|
772
1024
|
|
|
773
|
-
override fun setQueue(tracks: Array<Track>, startIndex: Double?,
|
|
1025
|
+
override fun setQueue(tracks: Array<Track>, startIndex: Double?, startPosition: Double?) =
|
|
774
1026
|
runBlockingOnMain {
|
|
775
|
-
player.setQueue(tracks, startIndex?.toInt() ?: 0,
|
|
1027
|
+
player.setQueue(tracks, startIndex?.toInt() ?: 0, ((startPosition ?: 0.0) * 1000).toLong())
|
|
776
1028
|
}
|
|
777
1029
|
|
|
778
1030
|
override fun setActiveTrackFavorited(favorited: Boolean) = runBlockingOnMain {
|
|
@@ -804,10 +1056,20 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
804
1056
|
// ============================================================================
|
|
805
1057
|
|
|
806
1058
|
override fun updateNowPlaying(update: NowPlayingUpdate?) = runBlockingOnMain {
|
|
807
|
-
player.updateNowPlaying(update)
|
|
1059
|
+
player.nowPlaying.updateNowPlaying(update)
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
override fun flashNowPlaying(update: NowPlayingUpdate, durationMs: Double) = runBlockingOnMain {
|
|
1063
|
+
player.nowPlaying.flashNowPlaying(update, durationMs)
|
|
808
1064
|
}
|
|
809
1065
|
|
|
810
|
-
override fun
|
|
1066
|
+
override fun clearNowPlayingFlash() = runBlockingOnMain {
|
|
1067
|
+
player.nowPlaying.clearNowPlayingFlash()
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
override fun getNowPlaying(): NowPlayingMetadata? = runBlockingOnMain {
|
|
1071
|
+
player.nowPlaying.getNowPlaying()
|
|
1072
|
+
}
|
|
811
1073
|
|
|
812
1074
|
// ============================================================================
|
|
813
1075
|
// MARK: Network Connectivity
|
|
@@ -824,13 +1086,15 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
824
1086
|
override fun setSystemVolume(volume: Double) = systemVolumeMonitor.setVolume(volume)
|
|
825
1087
|
|
|
826
1088
|
// ============================================================================
|
|
827
|
-
// MARK: External Audio Output
|
|
1089
|
+
// MARK: External Audio Output
|
|
828
1090
|
// ============================================================================
|
|
829
1091
|
|
|
830
|
-
override fun
|
|
1092
|
+
override fun getOutput(): Output? = outputMonitor.current
|
|
831
1093
|
|
|
832
|
-
override fun
|
|
833
|
-
|
|
1094
|
+
override fun supportsOutputSwitcher(): Boolean = OutputSwitcher.isSupported()
|
|
1095
|
+
|
|
1096
|
+
override fun openOutputPicker() {
|
|
1097
|
+
OutputSwitcher.open(context)
|
|
834
1098
|
}
|
|
835
1099
|
|
|
836
1100
|
// ============================================================================
|
|
@@ -838,34 +1102,34 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
838
1102
|
// ============================================================================
|
|
839
1103
|
|
|
840
1104
|
override fun getEqualizerSettings(): EqualizerSettings? = runBlockingOnMain {
|
|
841
|
-
player.
|
|
1105
|
+
player.equalizer.getSettings()
|
|
842
1106
|
}
|
|
843
1107
|
|
|
844
1108
|
override fun setEqualizerEnabled(enabled: Boolean) = runBlockingOnMain {
|
|
845
|
-
player.
|
|
1109
|
+
player.equalizer.setEnabled(enabled)
|
|
846
1110
|
}
|
|
847
1111
|
|
|
848
1112
|
override fun setEqualizerPreset(preset: String) = runBlockingOnMain {
|
|
849
|
-
player.
|
|
1113
|
+
player.equalizer.setPreset(preset)
|
|
850
1114
|
}
|
|
851
1115
|
|
|
852
1116
|
override fun setEqualizerLevels(levels: DoubleArray) = runBlockingOnMain {
|
|
853
|
-
player.
|
|
1117
|
+
player.equalizer.setLevels(levels)
|
|
854
1118
|
}
|
|
855
1119
|
|
|
856
1120
|
// ============================================================================
|
|
857
1121
|
// MARK: Sleep Timer
|
|
858
1122
|
// ============================================================================
|
|
859
1123
|
|
|
860
|
-
override fun getSleepTimer(): SleepTimer = runBlockingOnMain { player.
|
|
861
|
-
|
|
862
|
-
override fun setSleepTimer(seconds: Double) = runBlockingOnMain { player.setSleepTimer(seconds) }
|
|
1124
|
+
override fun getSleepTimer(): SleepTimer = runBlockingOnMain { player.sleepTimer.get() }
|
|
863
1125
|
|
|
864
|
-
override fun
|
|
865
|
-
player.
|
|
1126
|
+
override fun setSleepTimer(seconds: Double, fadeDuration: Double?) = runBlockingOnMain {
|
|
1127
|
+
player.sleepTimer.setAfter(seconds, fadeDuration)
|
|
866
1128
|
}
|
|
867
1129
|
|
|
868
|
-
override fun
|
|
1130
|
+
override fun setSleepTimerToEndOfTrack() = runBlockingOnMain { player.sleepTimer.setEndOfTrack() }
|
|
1131
|
+
|
|
1132
|
+
override fun clearSleepTimer(): Boolean = runBlockingOnMain { player.sleepTimer.clear() }
|
|
869
1133
|
|
|
870
1134
|
// ============================================================================
|
|
871
1135
|
// MARK: Battery Optimization (Android only)
|
|
@@ -945,6 +1209,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
945
1209
|
player.setCallbacks(callbacks)
|
|
946
1210
|
player.applyOptions(updateOptions)
|
|
947
1211
|
player.setup(setupOptions)
|
|
1212
|
+
applyPendingPlayerState(player)
|
|
948
1213
|
// Start observing network connectivity changes
|
|
949
1214
|
player.observeNetworkConnectivity(mainScope)
|
|
950
1215
|
// Set browser reference for media URL transformation
|
|
@@ -958,13 +1223,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
958
1223
|
}
|
|
959
1224
|
|
|
960
1225
|
// Wire up battery warning callback from service
|
|
961
|
-
connectedService?.onBatteryWarningPendingChanged =
|
|
962
|
-
post {
|
|
963
|
-
this@AudioBrowser.onBatteryWarningPendingChanged(
|
|
964
|
-
BatteryWarningPendingChangedEvent(pending)
|
|
965
|
-
)
|
|
966
|
-
}
|
|
967
|
-
}
|
|
1226
|
+
connectedService?.onBatteryWarningPendingChanged = onBatteryWarningPending
|
|
968
1227
|
|
|
969
1228
|
val sessionToken = SessionToken(context, ComponentName(context, Service::class.java))
|
|
970
1229
|
mediaBrowserFuture = MediaBrowser.Builder(context, sessionToken).buildAsync()
|
|
@@ -974,6 +1233,21 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
974
1233
|
}
|
|
975
1234
|
}
|
|
976
1235
|
|
|
1236
|
+
/**
|
|
1237
|
+
* Applies initial player state staged before the player existed. Consumed on apply so a later
|
|
1238
|
+
* re-setup doesn't replay stale state.
|
|
1239
|
+
*/
|
|
1240
|
+
private fun applyPendingPlayerState(player: Player) {
|
|
1241
|
+
pendingRepeatMode?.let {
|
|
1242
|
+
player.repeatMode = it
|
|
1243
|
+
pendingRepeatMode = null
|
|
1244
|
+
}
|
|
1245
|
+
pendingPlayWhenReady?.let {
|
|
1246
|
+
player.playWhenReady = it
|
|
1247
|
+
pendingPlayWhenReady = null
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
977
1251
|
override fun onServiceDisconnected(name: ComponentName) {
|
|
978
1252
|
mainScope.coroutineContext.cancelChildren()
|
|
979
1253
|
mediaBrowserFuture = null
|
|
@@ -1017,6 +1291,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
1017
1291
|
post { this@AudioBrowser.onPlaybackProgressUpdated(event) }
|
|
1018
1292
|
}
|
|
1019
1293
|
|
|
1294
|
+
override fun onPlaybackInterval() {
|
|
1295
|
+
post { this@AudioBrowser.onPlaybackInterval() }
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1020
1298
|
override fun onPlaybackPlayWhenReadyChanged(event: PlaybackPlayWhenReadyChangedEvent) {
|
|
1021
1299
|
post { this@AudioBrowser.onPlaybackPlayWhenReadyChanged(event) }
|
|
1022
1300
|
}
|
|
@@ -1166,12 +1444,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
1166
1444
|
return handled
|
|
1167
1445
|
}
|
|
1168
1446
|
|
|
1169
|
-
override fun onRemoteSetRating(event: RemoteSetRatingEvent) {
|
|
1170
|
-
post { this@AudioBrowser.onRemoteSetRating(event) }
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
1447
|
override fun onOptionsChanged(options: PlayerUpdateOptions) {
|
|
1174
|
-
|
|
1448
|
+
post { this@AudioBrowser.onOptionsChanged(options.toNitro()) }
|
|
1175
1449
|
}
|
|
1176
1450
|
|
|
1177
1451
|
override fun onFavoriteChanged(event: FavoriteChangedEvent) {
|
|
@@ -1194,4 +1468,45 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
|
|
|
1194
1468
|
post { this@AudioBrowser.onSleepTimerChanged(timer) }
|
|
1195
1469
|
}
|
|
1196
1470
|
}
|
|
1471
|
+
|
|
1472
|
+
companion object {
|
|
1473
|
+
// Process-wide car-connection state, from the androidx.car.app
|
|
1474
|
+
// CarConnection provider — the documented Android Auto / Automotive
|
|
1475
|
+
// connection signal, served by the car system app. Observed ONCE per
|
|
1476
|
+
// process: AudioBrowser instances are re-created on a JS reload, and a
|
|
1477
|
+
// per-instance observeForever would leak each one. The latest instance's
|
|
1478
|
+
// callback registers as the single notification target (mirrors the iOS
|
|
1479
|
+
// static + shared-instance pattern).
|
|
1480
|
+
@Volatile private var carConnected = false
|
|
1481
|
+
// Compare-and-set, not @Volatile: a disposing instance clears the slot only if it still owns
|
|
1482
|
+
// it, and a replacement may be claiming it at the same moment.
|
|
1483
|
+
private val carConnectionTarget = AtomicReference<((Boolean) -> Unit)?>(null)
|
|
1484
|
+
private var carConnectionObserverStarted = false
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* The instance the current JS runtime is using. Nitro hands out no registry, and the teardown
|
|
1488
|
+
* callback has nothing to dispose without one.
|
|
1489
|
+
*/
|
|
1490
|
+
private val current = AtomicReference<AudioBrowser?>(null)
|
|
1491
|
+
|
|
1492
|
+
/** Called by [AudioBrowserLifecycleModule] when the React instance is torn down. */
|
|
1493
|
+
internal fun disposeCurrent() {
|
|
1494
|
+
current.getAndSet(null)?.dispose()
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
private fun startCarConnectionObserver(context: Context, handler: Handler) {
|
|
1498
|
+
// Posted to main: LiveData observation is main-thread only, and the
|
|
1499
|
+
// single-start guard is then only ever touched from one thread.
|
|
1500
|
+
handler.post {
|
|
1501
|
+
if (carConnectionObserverStarted) return@post
|
|
1502
|
+
carConnectionObserverStarted = true
|
|
1503
|
+
CarConnection(context.applicationContext).type.observeForever { type ->
|
|
1504
|
+
val connected = type != CarConnection.CONNECTION_TYPE_NOT_CONNECTED
|
|
1505
|
+
if (connected == carConnected) return@observeForever
|
|
1506
|
+
carConnected = connected
|
|
1507
|
+
carConnectionTarget.get()?.invoke(connected)
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1197
1512
|
}
|