From 09c570b8067d3324170e525a1b05cb1bae31fbca Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Tue, 20 Jun 2023 14:10:57 +0200 Subject: [PATCH 01/18] fix(android): example production build (#2037) --- .../app/src/release/java/com/example/ReactNativeFlipper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/android/app/src/release/java/com/example/ReactNativeFlipper.java b/example/android/app/src/release/java/com/example/ReactNativeFlipper.java index b755b303c..c63f23c11 100644 --- a/example/android/app/src/release/java/com/example/ReactNativeFlipper.java +++ b/example/android/app/src/release/java/com/example/ReactNativeFlipper.java @@ -4,7 +4,7 @@ *

This source code is licensed under the MIT license found in the LICENSE file in the root * directory of this source tree. */ -package com.rndiffapp; +package com.example; import android.content.Context; import com.facebook.react.ReactInstanceManager; From dc13e8666b550eb5258b450e294ec8adcb7d20c4 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Tue, 20 Jun 2023 14:44:04 +0200 Subject: [PATCH 02/18] chore(version): Bump KotlinAudio/SwiftAudio version --- android/build.gradle | 2 +- react-native-track-player.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index fda9b8805..918d18b8b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -49,7 +49,7 @@ repositories { } dependencies { - implementation 'com.github.doublesymmetry:kotlinaudio:v2.0.0-rc6' + implementation 'com.github.doublesymmetry:kotlinaudio:v2.0.0-rc7' // used when building against local maven // implementation "com.github.doublesymmetry:kotlin-audio:1.2.2" diff --git a/react-native-track-player.podspec b/react-native-track-player.podspec index 92bda56c5..48f631c18 100644 --- a/react-native-track-player.podspec +++ b/react-native-track-player.podspec @@ -18,5 +18,5 @@ Pod::Spec.new do |s| s.swift_version = "4.2" s.dependency "React-Core" - s.dependency "SwiftAudioEx", "1.0.0-rc.3" + s.dependency "SwiftAudioEx", "1.0.0-rc.4" end From 18a17c744d789363b777f0967e7319239fd3cac2 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Tue, 20 Jun 2023 16:11:37 +0200 Subject: [PATCH 03/18] fix(android): improved foreground service handling (#2034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johnny Sørensen --- android/src/main/AndroidManifest.xml | 3 + .../trackplayer/module/MusicEvents.kt | 3 + .../trackplayer/service/MusicService.kt | 119 +++++++++++++++--- example/package.json | 3 + src/constants/Event.ts | 2 + src/interfaces/events/EventPayloadByEvent.ts | 16 +-- src/interfaces/events/EventsPayloadByEvent.ts | 16 +-- src/interfaces/events/PlayerErrorEvent.ts | 6 + 8 files changed, 134 insertions(+), 34 deletions(-) create mode 100644 src/interfaces/events/PlayerErrorEvent.ts diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 324363505..deabf14ce 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -5,6 +5,9 @@ + + diff --git a/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicEvents.kt b/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicEvents.kt index a785c9092..44da1d4bc 100644 --- a/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicEvents.kt +++ b/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicEvents.kt @@ -46,6 +46,9 @@ class MusicEvents(private val reactContext: ReactContext) : BroadcastReceiver() const val PLAYBACK_PROGRESS_UPDATED = "playback-progress-updated" const val PLAYBACK_ERROR = "playback-error" + // Other + const val PLAYER_ERROR = "player-error" + const val EVENT_INTENT = "com.doublesymmetry.trackplayer.event" } } 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 2ab5661ed..b7096a705 100644 --- a/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt +++ b/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt @@ -3,6 +3,7 @@ package com.doublesymmetry.trackplayer.service import android.app.* import android.content.Context import android.content.Intent +import android.content.pm.ServiceInfo import android.net.Uri import android.os.Binder import android.os.Build @@ -25,6 +26,7 @@ import com.doublesymmetry.trackplayer.model.Track import com.doublesymmetry.trackplayer.model.TrackAudioItem import com.doublesymmetry.trackplayer.module.MusicEvents import com.doublesymmetry.trackplayer.module.MusicEvents.Companion.EVENT_INTENT +import com.doublesymmetry.trackplayer.utils.AppForegroundTracker import com.doublesymmetry.trackplayer.utils.BundleUtils import com.doublesymmetry.trackplayer.utils.BundleUtils.setRating import com.facebook.react.HeadlessJsTaskService @@ -34,6 +36,7 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.flow import java.util.concurrent.TimeUnit import kotlin.system.exitProcess +import timber.log.Timber @MainThread class MusicService : HeadlessJsTaskService() { @@ -108,10 +111,13 @@ class MusicService : HeadlessJsTaskService() { ) } - val notification = NotificationCompat.Builder(this, name) + val notificationBuilder = NotificationCompat.Builder(this, name) .setPriority(PRIORITY_LOW) .setCategory(Notification.CATEGORY_SERVICE) - .build() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + notificationBuilder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) + } + val notification = notificationBuilder.build() startForeground(EMPTY_NOTIFICATION_ID, notification) @Suppress("DEPRECATION") stopForeground(true) @@ -151,6 +157,7 @@ class MusicService : HeadlessJsTaskService() { player = QueuedAudioPlayer(this@MusicService, playerConfig, bufferConfig, cacheConfig) player.automaticallyUpdateNotificationMetadata = automaticallyUpdateNotificationMetadata observeEvents() + setupForegrounding() } @MainThread @@ -467,6 +474,96 @@ class MusicService : HeadlessJsTaskService() { emit(MusicEvents.PLAYBACK_QUEUE_ENDED, bundle) } + @Suppress("DEPRECATION") + fun isForegroundService(): Boolean { + val manager = baseContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + for (service in manager.getRunningServices(Int.MAX_VALUE)) { + if (MusicService::class.java.name == service.service.className) { + return service.foreground + } + } + Timber.e("isForegroundService found no matching service") + return false + } + + @MainThread + private fun setupForegrounding() { + // Implementation based on https://github.com/Automattic/pocket-casts-android/blob/ee8da0c095560ef64a82d3a31464491b8d713104/modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackService.kt#L218 + var notificationId: Int? = null + var notification: Notification? = null + + fun startForegroundIfNecessary() { + if (isForegroundService()) { + Timber.d("skipping foregrounding as the service is already foregrounded") + return + } + if (notification == null) { + Timber.d("can't startForeground as the notification is null") + return + } + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground( + notificationId!!, + notification!!, + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + ) + } else { + startForeground(notificationId!!, notification) + } + Timber.d("notification has been foregrounded") + } catch (error: Exception) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + error is ForegroundServiceStartNotAllowedException + ) { + Timber.e( + "ForegroundServiceStartNotAllowedException: App tried to start a foreground Service when it was not allowed to do so.", + error + ) + emit(MusicEvents.PLAYER_ERROR, Bundle().apply { + putString("message", error.message) + putString("code", "android-foreground-service-start-not-allowed") + }); + } + } + } + + scope.launch { + event.notificationStateChange.collect { + when (it) { + is NotificationState.POSTED -> { + Timber.d("notification posted with id=%s, ongoing=%s", it.notificationId, it.ongoing) + notificationId = it.notificationId; + notification = it.notification; + } + else -> {} + } + } + } + scope.launch { + event.stateChange.collect { + when (it) { + AudioPlayerState.BUFFERING, + AudioPlayerState.PLAYING -> { + startForegroundIfNecessary() + } + AudioPlayerState.IDLE, + AudioPlayerState.STOPPED, + AudioPlayerState.ERROR, + AudioPlayerState.PAUSED -> { + val removeNotification = it != AudioPlayerState.PAUSED && it != AudioPlayerState.ERROR + if (removeNotification || isForegroundService()) { + @Suppress("DEPRECATION") + stopForeground(removeNotification) + Timber.d("stopped foregrounding") + } + } + else -> {} + } + } + } + } + @MainThread private fun observeEvents() { scope.launch { @@ -503,24 +600,6 @@ class MusicService : HeadlessJsTaskService() { } } - scope.launch { - event.notificationStateChange.collect { - when (it) { - is NotificationState.POSTED -> { - startForeground(it.notificationId, it.notification) - } - is NotificationState.CANCELLED -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - stopForeground(STOP_FOREGROUND_REMOVE) - } else { - @Suppress("DEPRECATION") - stopForeground(true) - } - } - } - } - } - scope.launch { event.onPlayerActionTriggeredExternally.collect { when (it) { diff --git a/example/package.json b/example/package.json index c2b68123c..5950ba5c3 100644 --- a/example/package.json +++ b/example/package.json @@ -6,6 +6,9 @@ "postinstall": "rm -rf node_modules/react-native-track-player/{example,node_modules}", "android": "react-native run-android", "android:ide": "open -a /Applications/Android\\ Studio.app ./android", + "android:uninstall": "adb uninstall com.example", + "android:release": "cd android && ./gradlew clean && ./gradlew bundleRelease && cd app/build/outputs/bundle/release && pwd && ls", + "android:install-release": "RD=android/app/build/outputs/bundle/release && (rm $RD/app.apks || true) && bundletool build-apks --bundle=$RD/app-release.aab --output=$RD/app.apks && (adb uninstall com.example || true) && bundletool install-apks --apks=$RD/app.apks", "ios": "react-native run-ios", "ios:ide": "open ios/*.xcworkspace/", "windows": "react-native run-windows", diff --git a/src/constants/Event.ts b/src/constants/Event.ts index a81853de6..f6f422374 100644 --- a/src/constants/Event.ts +++ b/src/constants/Event.ts @@ -1,4 +1,6 @@ export enum Event { + PlayerError = 'player-error', + /** Fired when the state of the player changes. */ PlaybackState = 'playback-state', /** Fired when a playback error occurs. */ diff --git a/src/interfaces/events/EventPayloadByEvent.ts b/src/interfaces/events/EventPayloadByEvent.ts index 1f590ff0d..83d4340ba 100644 --- a/src/interfaces/events/EventPayloadByEvent.ts +++ b/src/interfaces/events/EventPayloadByEvent.ts @@ -1,23 +1,25 @@ import { Event } from '../../constants'; import type { PlaybackState } from '../PlaybackState'; -import type { PlaybackErrorEvent } from './PlaybackErrorEvent'; -import type { PlaybackQueueEndedEvent } from './PlaybackQueueEndedEvent'; -import type { PlaybackTrackChangedEvent } from './PlaybackTrackChangedEvent'; import type { PlaybackActiveTrackChangedEvent } from './PlaybackActiveTrackChangedEvent'; +import type { PlaybackErrorEvent } from './PlaybackErrorEvent'; import type { PlaybackMetadataReceivedEvent } from './PlaybackMetadataReceivedEvent'; import type { PlaybackPlayWhenReadyChangedEvent } from './PlaybackPlayWhenReadyChangedEvent'; import type { PlaybackProgressUpdatedEvent } from './PlaybackProgressUpdatedEvent'; +import type { PlaybackQueueEndedEvent } from './PlaybackQueueEndedEvent'; +import type { PlaybackTrackChangedEvent } from './PlaybackTrackChangedEvent'; +import { PlayerErrorEvent } from './PlayerErrorEvent'; +import type { RemoteDuckEvent } from './RemoteDuckEvent'; +import type { RemoteJumpBackwardEvent } from './RemoteJumpBackwardEvent'; +import type { RemoteJumpForwardEvent } from './RemoteJumpForwardEvent'; import type { RemotePlayIdEvent } from './RemotePlayIdEvent'; import type { RemotePlaySearchEvent } from './RemotePlaySearchEvent'; -import type { RemoteSkipEvent } from './RemoteSkipEvent'; -import type { RemoteJumpForwardEvent } from './RemoteJumpForwardEvent'; -import type { RemoteJumpBackwardEvent } from './RemoteJumpBackwardEvent'; import type { RemoteSeekEvent } from './RemoteSeekEvent'; import type { RemoteSetRatingEvent } from './RemoteSetRatingEvent'; -import type { RemoteDuckEvent } from './RemoteDuckEvent'; +import type { RemoteSkipEvent } from './RemoteSkipEvent'; export interface EventPayloadByEvent { + [Event.PlayerError]: PlayerErrorEvent; [Event.PlaybackState]: PlaybackState; [Event.PlaybackError]: PlaybackErrorEvent; [Event.PlaybackQueueEnded]: PlaybackQueueEndedEvent; diff --git a/src/interfaces/events/EventsPayloadByEvent.ts b/src/interfaces/events/EventsPayloadByEvent.ts index bf3a1045d..09aa6b9ac 100644 --- a/src/interfaces/events/EventsPayloadByEvent.ts +++ b/src/interfaces/events/EventsPayloadByEvent.ts @@ -1,21 +1,22 @@ import { Event } from '../../constants'; import type { PlaybackState } from '../PlaybackState'; -import type { PlaybackErrorEvent } from './PlaybackErrorEvent'; -import type { PlaybackQueueEndedEvent } from './PlaybackQueueEndedEvent'; -import type { PlaybackTrackChangedEvent } from './PlaybackTrackChangedEvent'; import type { PlaybackActiveTrackChangedEvent } from './PlaybackActiveTrackChangedEvent'; +import type { PlaybackErrorEvent } from './PlaybackErrorEvent'; import type { PlaybackMetadataReceivedEvent } from './PlaybackMetadataReceivedEvent'; import type { PlaybackPlayWhenReadyChangedEvent } from './PlaybackPlayWhenReadyChangedEvent'; import type { PlaybackProgressUpdatedEvent } from './PlaybackProgressUpdatedEvent'; +import type { PlaybackQueueEndedEvent } from './PlaybackQueueEndedEvent'; +import type { PlaybackTrackChangedEvent } from './PlaybackTrackChangedEvent'; +import { PlayerErrorEvent } from './PlayerErrorEvent'; +import type { RemoteDuckEvent } from './RemoteDuckEvent'; +import type { RemoteJumpBackwardEvent } from './RemoteJumpBackwardEvent'; +import type { RemoteJumpForwardEvent } from './RemoteJumpForwardEvent'; import type { RemotePlayIdEvent } from './RemotePlayIdEvent'; import type { RemotePlaySearchEvent } from './RemotePlaySearchEvent'; -import type { RemoteSkipEvent } from './RemoteSkipEvent'; -import type { RemoteJumpForwardEvent } from './RemoteJumpForwardEvent'; -import type { RemoteJumpBackwardEvent } from './RemoteJumpBackwardEvent'; import type { RemoteSeekEvent } from './RemoteSeekEvent'; import type { RemoteSetRatingEvent } from './RemoteSetRatingEvent'; -import type { RemoteDuckEvent } from './RemoteDuckEvent'; +import type { RemoteSkipEvent } from './RemoteSkipEvent'; export interface EventsPayloadByEvent { [Event.PlaybackState]: PlaybackState & { type: Event.PlaybackState }; @@ -38,6 +39,7 @@ export interface EventsPayloadByEvent { [Event.PlaybackProgressUpdated]: PlaybackProgressUpdatedEvent & { type: Event.PlaybackProgressUpdated; }; + [Event.PlayerError]: PlayerErrorEvent & { type: Event.PlayerError }; [Event.RemotePlay]: { type: Event.RemotePlay }; [Event.RemotePlayId]: RemotePlayIdEvent & { type: Event.RemotePlayId }; [Event.RemotePlaySearch]: RemotePlaySearchEvent & { diff --git a/src/interfaces/events/PlayerErrorEvent.ts b/src/interfaces/events/PlayerErrorEvent.ts new file mode 100644 index 000000000..a7613fdf8 --- /dev/null +++ b/src/interfaces/events/PlayerErrorEvent.ts @@ -0,0 +1,6 @@ +export interface PlayerErrorEvent { + /** The error code */ + code: 'android-foreground-service-start-not-allowed'; + /** The error message */ + message: string; +} From 32baacdfdcbf5edb81a8a3199fdf30896dcce0cc Mon Sep 17 00:00:00 2001 From: Tatiana Kapos Date: Fri, 23 Jun 2023 22:56:34 -0700 Subject: [PATCH 04/18] fix(bump minimum windows version): bump windows WindowsTargetPlatformVersion (#2010) --- windows/RNTrackPlayer/RNTrackPlayer.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/RNTrackPlayer/RNTrackPlayer.vcxproj b/windows/RNTrackPlayer/RNTrackPlayer.vcxproj index ebee183af..81600723e 100644 --- a/windows/RNTrackPlayer/RNTrackPlayer.vcxproj +++ b/windows/RNTrackPlayer/RNTrackPlayer.vcxproj @@ -14,7 +14,7 @@ Windows Store 10.0 10.0.18362.0 - 10.0.16299.0 + 10.0.17763.0 From 197d3fca04de0d5ad0f749f68ddd0b15e41e4106 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 26 Jun 2023 08:46:10 +0200 Subject: [PATCH 05/18] Bumpt to v4.0.0-rc05 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7506fccc5..1d917726b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# [4.0.0-rc04](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc02...v4.0.0-rc03) (2023-03-28) + +* **ios:** Fix crash on getting current item +* **android:** Improve preciseness of seeking +* **android:** Improve handling of service foregrounding + # [4.0.0-rc03](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc02...v4.0.0-rc03) (2023-03-28) * **android:** Fixes compilation issue due to uses of Lifecycle (updates kotlin gradle plugin) diff --git a/package.json b/package.json index 9c56a66fd..60c20260b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-track-player", - "version": "4.0.0-rc03", + "version": "4.0.0-rc05", "description": "A fully fledged audio module created for music apps", "main": "lib/index.js", "types": "lib/index.d.ts", From 7cb953c548b046f13c5e6bcc16b458a00da1e7b4 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 26 Jun 2023 09:10:47 +0200 Subject: [PATCH 06/18] Update CHANGELOG.md (#2042) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d917726b..360282160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# [4.0.0-rc04](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc02...v4.0.0-rc03) (2023-03-28) +# [4.0.0-rc05](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc04...v4.0.0-rc05) (2023-03-28) * **ios:** Fix crash on getting current item * **android:** Improve preciseness of seeking From 70f836472ca52165984502fb331747d4bb4eab30 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 26 Jun 2023 09:11:25 +0200 Subject: [PATCH 07/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360282160..f800f1a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# [4.0.0-rc05](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc04...v4.0.0-rc05) (2023-03-28) +# [4.0.0-rc05](https://github.com/doublesymmetry/react-native-track-player/compare/v4.0.0-rc04...v4.0.0-rc05) (2023-06-26) * **ios:** Fix crash on getting current item * **android:** Improve preciseness of seeking From 83319792225a9197a3187a33543a341aebdf70b3 Mon Sep 17 00:00:00 2001 From: Philip Su <39933441+fivecar@users.noreply.github.com> Date: Wed, 28 Jun 2023 00:29:54 -0700 Subject: [PATCH 08/18] feat: useIsPlaying hook and isPlaying() method (#2040) * feat: useIsPlaying hook and isPlaying() method * fix: empty line removed at end of file for prettier * fix: added documentation and also updated API to reflect undefined possibilities * fix: account for code review feedback We now return undefined in all cases where the state is uncertain, and we also return both `playing` and `bufferingDuringPlay` from `isPlaying()`. --- docs/docs/guides/play-button.md | 24 ++++++++++ example/src/components/PlayPauseButton.tsx | 26 +++------- example/src/components/PlayerControls.tsx | 2 +- src/hooks/index.ts | 1 + src/hooks/useIsPlaying.ts | 55 ++++++++++++++++++++++ 5 files changed, 87 insertions(+), 21 deletions(-) create mode 100644 docs/docs/guides/play-button.md create mode 100644 src/hooks/useIsPlaying.ts diff --git a/docs/docs/guides/play-button.md b/docs/docs/guides/play-button.md new file mode 100644 index 000000000..844d1c567 --- /dev/null +++ b/docs/docs/guides/play-button.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 4 +--- + +# Play Buttons + +UI often needs to display a Play button that changes between three states: + +1. Play +2. Pause +3. Spinner (e.g. if playback is being attempted, but sound is paused due to buffering) + +Implementing this correctly will take a bit of care. For instance, `usePlaybackState` can return `State.Buffering` even if playback is currently paused. `usePlayWhenReady` is one way to check if the player is attempting to play, but can return true even if `PlaybackState` is `State.Error` or `State.Ended`. + +To determine how to render a Play button in its three states correctly, do the following: + +* Render the button as a spinner if `playWhenReady` and `state === State.Loading || state === State.Buffering` +* Else render the button as being in the Playing state if `playWhenReady && !(state === State.Error || state === State.Buffering)` +* Otherwise render the button as being in the Paused state + +To help with this logic, the API has two utilities: + +1. The `useIsPlaying()` hook. This returns `{playing: boolean | undefined, bufferingDuringPlay: boolean | undefined}`, which you can consult to render your play button correctly. You should render a spinner if `bufferingDuringPlay === true`; otherwise render according to `playing`. Values are `undefined` if the player isn't yet in a state where they can be determined. +2. The `async isPlaying()` function, which returns the same result as `useIsPlaying()`, but can be used outside of React components (i.e. without hooks). Note that you can't easily just instead call `getPlaybackState()` to determine the same answer, unless you've accounted for the issues mentioned above. \ No newline at end of file diff --git a/example/src/components/PlayPauseButton.tsx b/example/src/components/PlayPauseButton.tsx index 9a528f8fd..0e96ff0ba 100644 --- a/example/src/components/PlayPauseButton.tsx +++ b/example/src/components/PlayPauseButton.tsx @@ -1,33 +1,19 @@ import React from 'react'; import { ActivityIndicator, StyleSheet, View } from 'react-native'; -import TrackPlayer, { - State, - usePlayWhenReady, -} from 'react-native-track-player'; -import { useDebouncedValue } from '../hooks'; +import TrackPlayer, { useIsPlaying } from 'react-native-track-player'; import { Button } from './Button'; -export const PlayPauseButton: React.FC<{ - state: State | undefined; -}> = ({ state }) => { - const playWhenReady = usePlayWhenReady(); - const isLoading = useDebouncedValue( - state === State.Loading || state === State.Buffering, - 250 - ); +export const PlayPauseButton: React.FC = () => { + const { playing, bufferingDuringPlay } = useIsPlaying(); - const isErrored = state === State.Error; - const isEnded = state === State.Ended; - const showPause = playWhenReady && !(isErrored || isEnded); - const showBuffering = playWhenReady && isLoading; - return showBuffering ? ( + return bufferingDuringPlay ? ( ) : (