From 53440a39189eb02431600978acebc9c2cdb5e234 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Fri, 16 May 2025 11:51:08 +0800 Subject: [PATCH] fix(mobile): player event on android https://github.com/doublesymmetry/react-native-track-player/pull/2370 --- patches/react-native-track-player.patch | 173 +++++++++++++----------- pnpm-lock.yaml | 6 +- 2 files changed, 100 insertions(+), 79 deletions(-) diff --git a/patches/react-native-track-player.patch b/patches/react-native-track-player.patch index 42950ff2f..d405d7fec 100644 --- a/patches/react-native-track-player.patch +++ b/patches/react-native-track-player.patch @@ -4,24 +4,24 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 +++ b/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt @@ -251,8 +251,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun updateOptions(data: ReadableMap?, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun updateOptions(data: ReadableMap?, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + val options = Arguments.toBundle(data) - + @@ -264,14 +264,14 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun add(data: ReadableArray?, insertBeforeIndex: Int, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun add(data: ReadableArray?, insertBeforeIndex: Int, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + try { val tracks = readableArrayToTrackList(data); if (insertBeforeIndex < -1 || insertBeforeIndex > musicService.tracks.size) { @@ -33,7 +33,7 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 musicService.add( @@ -285,11 +285,11 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun load(data: ReadableMap?, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -48,7 +48,7 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 if (bundle is Bundle) { @@ -301,15 +301,15 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun move(fromIndex: Int, toIndex: Int, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -57,7 +57,7 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 musicService.move(fromIndex, toIndex) callback.resolve(null) } - + @ReactMethod - fun remove(data: ReadableArray?, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -76,262 +76,262 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 indexes.add(index) } @@ -332,8 +332,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM - + @ReactMethod fun updateMetadataForTrack(index: Int, map: ReadableMap?, callback: Promise) = - scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + if (index < 0 || index >= musicService.tracks.size) { callback.reject("index_out_of_bounds", "The index is out of bounds") @@ -348,8 +348,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun updateNowPlayingMetadata(map: ReadableMap?, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun updateNowPlayingMetadata(map: ReadableMap?, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + if (musicService.tracks.isEmpty()) callback.reject("no_current_item", "There is no current item in the player") @@ -364,8 +364,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun clearNowPlayingMetadata(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun clearNowPlayingMetadata(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + if (musicService.tracks.isEmpty()) callback.reject("no_current_item", "There is no current item in the player") @@ -375,16 +375,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun removeUpcomingTracks(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun removeUpcomingTracks(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.removeUpcomingTracks() callback.resolve(null) } - + @ReactMethod - fun skip(index: Int, initialTime: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun skip(index: Int, initialTime: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.skip(index) - + @@ -396,8 +396,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun skipToNext(initialTime: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun skipToNext(initialTime: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.skipToNext() - + @@ -409,8 +409,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun skipToPrevious(initialTime: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun skipToPrevious(initialTime: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.skipToPrevious() - + @@ -422,8 +422,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun reset(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun reset(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.stop() delay(300) // Allow playback to stop @@ -433,116 +433,116 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun play(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun play(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.play() callback.resolve(null) } - + @ReactMethod - fun pause(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun pause(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.pause() callback.resolve(null) } - + @ReactMethod - fun stop(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun stop(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.stop() callback.resolve(null) } - + @ReactMethod - fun seekTo(seconds: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun seekTo(seconds: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.seekTo(seconds) callback.resolve(null) } - + @ReactMethod - fun seekBy(offset: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun seekBy(offset: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.seekBy(offset) callback.resolve(null) } - + @ReactMethod - fun retry(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun retry(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.retry() callback.resolve(null) } - + @ReactMethod - fun setVolume(volume: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun setVolume(volume: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.setVolume(volume) callback.resolve(null) } - + @ReactMethod - fun getVolume(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getVolume(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getVolume()) } - + @ReactMethod - fun setRate(rate: Float, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun setRate(rate: Float, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.setRate(rate) callback.resolve(null) } - + @ReactMethod - fun getRate(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getRate(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getRate()) } - + @ReactMethod - fun setRepeatMode(mode: Int, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun setRepeatMode(mode: Int, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.setRepeatMode(RepeatMode.fromOrdinal(mode)) callback.resolve(null) } - + @ReactMethod - fun getRepeatMode(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getRepeatMode(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getRepeatMode().ordinal) } - + @ReactMethod - fun setPlayWhenReady(playWhenReady: Boolean, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun setPlayWhenReady(playWhenReady: Boolean, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + musicService.playWhenReady = playWhenReady callback.resolve(null) } - + @ReactMethod - fun getPlayWhenReady(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getPlayWhenReady(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.playWhenReady) } - + @ReactMethod - fun getTrack(index: Int, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getTrack(index: Int, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + if (index >= 0 && index < musicService.tracks.size) { callback.resolve(Arguments.fromBundle(musicService.tracks[index].originalItem)) @@ -552,15 +552,15 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun getQueue(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getQueue(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(Arguments.fromList(musicService.tracks.map { it.originalItem })) } - + @ReactMethod - fun setQueue(data: ReadableArray?, callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun setQueue(data: ReadableArray?, callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + try { musicService.clear() @@ -572,16 +572,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun getActiveTrackIndex(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -341,7 +341,7 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 if (musicService.tracks.isEmpty()) null else musicService.getCurrentTrackIndex() ) } - + @ReactMethod - fun getActiveTrack(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -352,34 +352,34 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 else Arguments.fromBundle( @@ -591,29 +591,29 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun getDuration(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getDuration(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getDurationInSeconds()) } - + @ReactMethod - fun getBufferedPosition(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getBufferedPosition(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getBufferedPositionInSeconds()) } - + @ReactMethod - fun getPosition(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch + fun getPosition(callback: Promise) = launchInScope { + if (verifyServiceBoundOrReject(callback)) return@launchInScope - + callback.resolve(musicService.getPositionInSeconds()) } - + @ReactMethod - fun getProgress(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -390,7 +390,7 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 bundle.putDouble("position", musicService.getPositionInSeconds()); @@ -622,8 +622,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM } - + @ReactMethod - fun getPlaybackState(callback: Promise) = scope.launch { - if (verifyServiceBoundOrReject(callback)) return@launch @@ -408,20 +408,41 @@ index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..5f434a6931977d4b56d359295d53ef02 + } } diff --git a/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt b/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt -index 9d6d869efcece065618d4f2cefdc8c54831af9ed..1bd9bf68f158079a4625254232dd1430340364f5 100644 +index 9d6d869efcece065618d4f2cefdc8c54831af9ed..37f41fd3fcd8dcb7b5b54c0ad3f207ddeb6d961f 100644 --- a/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt +++ b/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt -@@ -760,8 +760,9 @@ class MusicService : HeadlessJsTaskService() { +@@ -741,9 +741,7 @@ class MusicService : HeadlessJsTaskService() { + + @MainThread + private fun emit(event: String, data: Bundle? = null) { +- reactNativeHost.reactInstanceManager.currentReactContext +- ?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) +- ?.emit(event, data?.let { Arguments.fromBundle(it) }) ++ reactContext?.emitDeviceEvent(event, data?.let { Arguments.fromBundle(it) }) + } + + @MainThread +@@ -751,17 +749,16 @@ class MusicService : HeadlessJsTaskService() { + val payload = Arguments.createArray() + data.forEach { payload.pushMap(Arguments.fromBundle(it)) } + +- reactNativeHost.reactInstanceManager.currentReactContext +- ?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) +- ?.emit(event, payload) ++ reactContext?.emitDeviceEvent(event, payload) + } + + override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig { return HeadlessJsTaskConfig(TASK_KEY, Arguments.createMap(), 0, true) } - + + // https://github.com/doublesymmetry/react-native-track-player/pull/2451 @MainThread - override fun onBind(intent: Intent?): IBinder { + override fun onBind(intent: Intent): IBinder { return binder } - + diff --git a/ios/RNTrackPlayer/RNTrackPlayerBridge.m b/ios/RNTrackPlayer/RNTrackPlayerBridge.m index 7741994191921ed86f7577f7ce589b3939d9dd49..776c09ec0ae450daa4dc9031ccd3ff4baba016d3 100644 --- a/ios/RNTrackPlayer/RNTrackPlayerBridge.m @@ -429,7 +450,7 @@ index 7741994191921ed86f7577f7ce589b3939d9dd49..776c09ec0ae450daa4dc9031ccd3ff4b @@ -151,17 +151,4 @@ RCT_EXTERN_METHOD(updateNowPlayingMetadata:(NSDictionary *)metadata resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject); - + -RCT_EXTERN_METHOD(getSleepTimerProgress:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject); - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d22165cf3..1c5d4e2ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,7 +66,7 @@ patchedDependencies: hash: 8a10ea43dd4d194a581d3d765a1eb3e864441b968d5d5729382a9986f710ef32 path: patches/react-native-sheet-transitions.patch react-native-track-player: - hash: 6f009c7ac1d813832cf0aa58d6fc600c57bc5e1b566d277dbcfe115d06f1f2d3 + hash: d011fdd16d124609523059323ac7614e3999174a9c7b21074c1124d7075650fb path: patches/react-native-track-player.patch workbox-precaching: hash: 51e57c78af317e292b43fca4b72f57b247b7b1a1faa8b03c8881ddb798c7c52f @@ -1047,7 +1047,7 @@ importers: version: 15.11.2(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0) react-native-track-player: specifier: 4.1.1 - version: 4.1.1(patch_hash=6f009c7ac1d813832cf0aa58d6fc600c57bc5e1b566d277dbcfe115d06f1f2d3)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0) + version: 4.1.1(patch_hash=d011fdd16d124609523059323ac7614e3999174a9c7b21074c1124d7075650fb)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0) react-native-uikit-colors: specifier: 0.5.2 version: 0.5.2(nativewind@4.1.23(react-native-reanimated@3.17.4(@babel/core@7.26.10)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native-safe-area-context@5.3.0(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native-svg@15.11.2(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.15.3)(typescript@5.8.3))))(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.15.3)(typescript@5.8.3))) @@ -29601,7 +29601,7 @@ snapshots: react-native: 0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5) warn-once: 0.1.1 - react-native-track-player@4.1.1(patch_hash=6f009c7ac1d813832cf0aa58d6fc600c57bc5e1b566d277dbcfe115d06f1f2d3)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0): + react-native-track-player@4.1.1(patch_hash=d011fdd16d124609523059323ac7614e3999174a9c7b21074c1124d7075650fb)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0): dependencies: react: 19.0.0 react-native: 0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)