How it works...

In step 4, we initialized options on the Audio component once the app finished loading via the componentDidMount method. The Audio component's setAudioModeAsync method takes an option object as its only parameter.

Let's review some of the options we used in this recipe:

  • interruptionModeIOS and interruptionModeAndroid set how the audio in your app should interact with the audio from other applications on the device. We used the Audio component's INTERRUPTION_MODE_IOS_DO_NOT_MIX and INTERRUPTION_MODE_ANDROID_DO_NOT_MIX enums, respectively, to declare that our app's audio should interrupt any other applications playing audio.
  • playsInSilentModeIOS is a Boolean that determines whether your app should play audio when the device is in silent mode.
  • shouldDuckAndroid is a Boolean that determines whether your app's audio should lower in volume (duck) when audio from another app interrupts your app. While this setting defaults to true, I've added it to the recipe so that you're aware that it's an option.

In step 5, we defined the loadAudio method, which performs the heavy lifting in this recipe. First, we created a new instance of the Audio.Sound class and saved it to the  playbackInstance variable for later use. Next, we set the source and status variables that will be passed into the loadAsync function on the playbackInstance for actually loading the audio file. In the source object, we set the uri property to the corresponding uri property on the object in the playlist array at the index stored in this.state.currentTrackIndex. In the status object, we set the volume to the volume value saved on state, and set shouldPlay, a Boolean that determines whether the audio should be playing, initially to this.state.isPlaying. And, since we want to stream the remote MP3 file instead of waiting for the entire file to download, we pass false the third, downloadFirst, parameter.

Before calling the loadAsync method, we first called setOnPlaybackStatusUpdate of playbackInstance, which takes a callback function that should be called when the state of playbackInstance has changed. We defined that handler in step 6. The handler simply saves the isBuffering value from the callback's status parameter to the isBuffering property of state, which will fire a rerender, updating the 'Buffering...' message in the UI accordingly.

In step 7, we defined the handlePlayPause function for toggling play and pause functionality in the app. If there's a track playing, this.state.isPlaying will be true, so we'll call the pauseAsync function on the playbackInstance otherwise, we'll call playAsync to start playing the audio again. Once we've played or paused, we update the value of isPlaying on state.

In step 8 and step 9, we created the functions that handle skipping to the next and previous tracks. Each of these functions increases or decreases the value of this.state.currentTrackIndex as appropriate, so that by the time this.loadAudio is called at the bottom of each function, it will load the track associated with the object in the playlist array at the new index.