Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
aca34d7
Modernize package structure (#654)
dcvz Jul 6, 2019
99f8524
[ios] Update base player
dcvz Jul 6, 2019
1607df4
[ios] Update podspec for RN 0.60
dcvz Jul 6, 2019
3f26e6a
[iOS] Fix issue conforming to protocol
dcvz Jul 6, 2019
5daf491
Improve hook for getting progress
dcvz Jul 10, 2019
434baf7
Rename `bufferedPosition` to `buffered` in progress hook
dcvz Jul 10, 2019
53b765f
Export `useProgress hook
dcvz Jul 10, 2019
56961c3
[ios] Fix setting of `remoteCommands`
dcvz Jul 16, 2019
1a6a627
[docs] Fixed misnamed export typo in Documentation.md (#756)
jonnyberanek Oct 20, 2019
9b60f3f
[android] Updated androidx libraries
Guichaguri Oct 21, 2019
00ffdcc
[androidFix: Bad notification for startForeground: java.lang.RuntimeE…
biomancer Dec 27, 2019
ecf98fb
[android] Fixed array index out of bounds crash (#778)
skahack Dec 27, 2019
1c3fefc
[iOSCall `playback-track-changed` for last track (#772)
high-performance-hery Dec 27, 2019
a53707d
Fixed noisy receiver not being disposed after stopping the player
Guichaguri Dec 27, 2019
5771543
Remove now useless configuration files
Guichaguri Dec 27, 2019
bf4cfb8
[android] Prevents unbound calls to destroy
Guichaguri Dec 27, 2019
2158450
[docs] Make the getDuration() note a bit clearer
Guichaguri Dec 27, 2019
5a5c709
Merge branch 'dev' into v2
Guichaguri Dec 27, 2019
d0f782f
Split Metadata Functionality (#634)
dcvz Dec 27, 2019
706585d
[docs] Added cleartext HTTP to the troubleshooting section
Guichaguri Dec 29, 2019
27517c9
Fix(d.ts): Track - add headers field (#824)
ovr Jan 4, 2020
c34d240
[ios] Disable automaticallyPlayNextSong in SwiftAudio so that we can …
curiousdustin Jan 4, 2020
a92588e
Merge branch 'dev' into v2
Guichaguri Jan 4, 2020
85e2898
[android] Updated androidx core
Guichaguri Jan 4, 2020
8698cff
[android] Fix the last known window after removing a track
Guichaguri Jan 4, 2020
170c14a
[iOS] Updated SwiftAudio submodule
Guichaguri Jan 8, 2020
2b76ef7
[ios] Update base player
dcvz Jul 6, 2019
e270f7d
[iOS] Fix issue conforming to protocol
dcvz Jul 6, 2019
e76f25e
[iOS] Updated SwiftAudio submodule
Guichaguri Jan 8, 2020
e2cf596
Merge branch 'dev' into v2
Guichaguri Jan 8, 2020
b721a49
Merge pull request #3 from react-native-kit/dev
lukebaker Jan 14, 2020
c5353c6
Merge branch 'dev' into upstream-agathon
lukebaker Jan 14, 2020
93c474b
Merge branch 'v2' of https://github.com/react-native-kit/react-native…
jeffmon Jan 16, 2020
1d6af19
Fix remote controls on iOS
jeffmon Jan 16, 2020
8627fd2
Revert "Merge branch 'v2' of https://github.com/react-native-kit/reac…
jeffmon Jan 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .jshintrc

This file was deleted.

12 changes: 0 additions & 12 deletions .tern-project

This file was deleted.

9 changes: 5 additions & 4 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'

def exoPlayerVersion = safeExtGet("exoPlayerVersion", '2.10.1');
def exoPlayerVersion = safeExtGet("exoPlayerVersion", '2.10.1')

// ExoPlayer Core
implementation "com.google.android.exoplayer:exoplayer-core:${exoPlayerVersion}"
Expand All @@ -87,8 +87,9 @@ dependencies {
compileOnly "com.google.android.exoplayer:exoplayer-smoothstreaming:${exoPlayerVersion}"
}

// Make sure we're using at least the support library 27.1.1
implementation "com.android.support:support-compat:${safeExtGet('supportLibVersion', '27.1.1')}"
implementation "com.android.support:support-media-compat:${safeExtGet('supportLibVersion', '27.1.1')}"
// Make sure we're using androidx
implementation "androidx.core:core:1.2.0-beta01"
implementation "androidx.media:media:1.1.0"
implementation "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0-alpha01"
implementation "com.github.bumptech.glide:glide:4.7.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ public void setupPlayer(ReadableMap data, final Promise promise) {

@ReactMethod
public void destroy() {
// Ignore if it was already destroyed
if (binder == null && !connecting) return;

try {
if(binder != null) {
binder.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ public void onPlay() {
requestFocus();

if(!receivingNoisyEvents) {
service.registerReceiver(noisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
receivingNoisyEvents = true;
service.registerReceiver(noisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}

if(!wakeLock.isHeld()) wakeLock.acquire();
Expand Down Expand Up @@ -182,6 +182,12 @@ public void onPause() {
public void onStop() {
Log.d(Utils.LOG, "onStop");

// Unregisters the noisy receiver
if(receivingNoisyEvents) {
service.unregisterReceiver(noisyReceiver);
receivingNoisyEvents = false;
}

// Release the wake and the wifi locks
if(wakeLock.isHeld()) wakeLock.release();
if(wifiLock.isHeld()) wifiLock.release();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.guichaguri.trackplayer.service;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
Expand All @@ -18,6 +16,7 @@
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
import com.guichaguri.trackplayer.service.Utils;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -74,11 +73,7 @@ private void onStartForeground() {

// Checks whether there is a React activity
if(reactContext == null || !reactContext.hasCurrentActivity()) {
String channel = null;

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channel = NotificationChannel.DEFAULT_CHANNEL_ID;
}
String channel = Utils.getNotificationChannel((Context) this);

// Sets the service to foreground with an empty notification
startForeground(1, new NotificationCompat.Builder(this, channel).build());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.guichaguri.trackplayer.service;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.media.RatingCompat;
import android.support.v4.media.session.PlaybackStateCompat;
Expand Down Expand Up @@ -156,4 +159,18 @@ public static int getInt(Bundle data, String key, int defaultValue) {
}
return defaultValue;
}

public static String getNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
Utils.NOTIFICATION_CHANNEL,
"MusicService",
NotificationManager.IMPORTANCE_DEFAULT
);
channel.setShowBadge(false);
channel.setSound(null, null);
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
}
return Utils.NOTIFICATION_CHANNEL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,8 @@ public MetadataManager(MusicService service, MusicManager manager) {
this.service = service;
this.manager = manager;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(Utils.NOTIFICATION_CHANNEL, "Playback", NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(false);
channel.setSound(null, null);

NotificationManager not = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
not.createNotificationChannel(channel);
}

this.builder = new NotificationCompat.Builder(service, Utils.NOTIFICATION_CHANNEL);
String channel = Utils.getNotificationChannel((Context) service);
this.builder = new NotificationCompat.Builder(service, channel);
this.session = new MediaSessionCompat(service, "TrackPlayer", null, null);

session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public void onPositionDiscontinuity(int reason) {
Log.d(Utils.LOG, "onPositionDiscontinuity: " + reason);

if(lastKnownWindow != player.getCurrentWindowIndex()) {
Track previous = lastKnownWindow == C.INDEX_UNSET ? null : queue.get(lastKnownWindow);
Track previous = lastKnownWindow == C.INDEX_UNSET || lastKnownWindow >= queue.size() ? null : queue.get(lastKnownWindow);
Track next = getCurrentTrack();

// Track changed because it ended
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public void remove(List<Integer> indexes, Promise promise) {
} else {
source.removeMediaSource(index);
}

// Fix the window index
if (index < lastKnownWindow) {
lastKnownWindow--;
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions docs/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ Gets the duration of the current track in seconds.

Note: `react-native-track-player` is a streaming library, which means it slowly buffers the track and doesn't know exactly when it ends.
The duration returned by this function is determined through various tricks and *may not be exact or may not be available at all*.
We highly recommend you to retrieve the duration from a database and feed it to the `duration` parameter in the [Track Object](#track-object).

You should **not** trust this function. You should retrieve the duration from a database and feed it to the `duration` parameter in the [Track Object](#track-object).

**Returns:** `Promise<number>`

Expand All @@ -322,10 +323,10 @@ Gets the state of the player.

## Events

All event types are made available through the named export `TrackPlayerEventTypes`:
All event types are made available through the named export `TrackPlayerEvents`:

```js
import { TrackPlayerEventTypes } from 'react-native-track-player';
import { TrackPlayerEvents } from 'react-native-track-player';
```

### Media Controls
Expand Down
15 changes: 15 additions & 0 deletions docs/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,20 @@ android {
#### Android: `com.facebook.react.common.JavascriptException: No task registered for key TrackPlayer`
The playback service requires a headless task to be registered. You have to register it with `registerPlaybackService`.

#### Android: `Error: Attribute XXX from [androidx.core:core:XXX] is also present at [com.android.support:support-compat:XXX]`
This error occurs when you're mixing both AndroidX and the Support Library in the same project.

You have to either upgrade everything to AndroidX or downgrade everything to the support library.


* For react-native-track-player, the last version to run the support library is **1.1.4** and the first version to run AndroidX is **1.2.0**.
* For react-native, the last version to run the support library is **0.59** and the first version to run AndroidX is **0.60**.

You can also use [jetifier](https://github.com/mikehardy/jetifier#usage-for-source-files) to convert all of the native code to use only one of them.

#### Android: Cleartext HTTP traffic not permitted

Since API 28, Android disables traffic without TLS. To fix the issue you have to use `https` or [enable clear text traffic](https://stackoverflow.com/a/50834600).

## Next
You can choose the build preferences for your app using `track-player.json`. See more [here](https://react-native-kit.github.io/react-native-track-player/build-preferences/).
10 changes: 7 additions & 3 deletions ios/RNTrackPlayer/Models/Track.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import MediaPlayer
import AVFoundation

class Track: NSObject, AudioItem, TimePitching, Authorizing {
class Track: NSObject, AudioItem, TimePitching, AssetOptionsProviding {
let id: String
let url: MediaURL

Expand Down Expand Up @@ -133,8 +133,12 @@ class Track: NSObject, AudioItem, TimePitching, Authorizing {

// MARK: - Authorizing Protocol

func getHeaders() -> [String : Any] {
return headers ?? [:]
func getAssetOptions() -> [String: Any] {
if let headers = headers {
return ["AVURLAssetHTTPHeaderFieldsKey": headers]
}

return [:]
}

}
32 changes: 25 additions & 7 deletions ios/RNTrackPlayer/RNTrackPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public class RNTrackPlayer: RCTEventEmitter {
private lazy var player: QueuedAudioPlayer = {
let player = QueuedAudioPlayer()
player.bufferDuration = 1

// disable auto advance, so that we can control the order of
// operations in order to send accurate event data
player.automaticallyPlayNextSong = false

return player
}()

Expand Down Expand Up @@ -185,18 +190,31 @@ public class RNTrackPlayer: RCTEventEmitter {
player.event.playbackEnd.addListener(self) { [weak self] reason in
guard let `self` = self else { return }

if reason == .playedUntilEnd && self.player.nextItems.count == 0 {
self.sendEvent(withName: "playback-queue-ended", body: [
"track": (self.player.currentItem as? Track)?.id,
"position": self.player.currentTime,
])
} else if reason == .playedUntilEnd {
self.sendEvent(withName: "playback-track-changed", body: [
if reason == .playedUntilEnd {
// playbackEnd is called twice at the end of a track;
// we ignore .skippedToNext and only fire an event
// for .playedUntilEnd
// nextTrack might be nil if there are no more, but still send the event for consistency
self.sendEvent(withName: "playback-track-changed", body: [
"track": (self.player.currentItem as? Track)?.id,
"position": self.player.currentTime,
"nextTrack": (self.player.nextItems.first as? Track)?.id,
])

if self.player.nextItems.count == 0 {
// fire an event for the queue ending
self.sendEvent(withName: "playback-queue-ended", body: [
"track": (self.player.currentItem as? Track)?.id,
"position": self.player.currentTime,
])
} else {
// we are not using automaticallyPlayNextSong on the player in order
// to be in control of specifically when the above events are sent
// so, attempt to go to the next track now
try? self.player.next()
}
}

}

player.remoteCommandController.handleChangePlaybackPositionCommand = { [weak self] event in
Expand Down
Loading