Skip to content

Commit 8e854f6

Browse files
committed
Exposed the API to the JS module and general fixes
1 parent 9fd394f commit 8e854f6

16 files changed

+140
-117
lines changed

.jshintrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"esversion": 6,
3+
"node": true,
4+
"eqnull": true,
5+
"-W041": false,
6+
"-W069": false,
7+
"globals": {
8+
"path": true,
9+
"load": true
10+
}
11+
}

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ This is a work-in-progress project not ready for production yet.
1111

1212
* [x] Multi-player support
1313
* [x] Android MediaSession support
14-
* [x] Android MediaBrowser support (required for Android Auto)
14+
* [ ] Android MediaBrowser support (required for Android Auto)
1515
* [x] Android MediaStyle Notification support
1616
* [x] Media buttons fully handled (bluetooth, smartwatches, headphones)
1717
* [x] Android MediaPlayer support
18-
* [ ] *Optional* ExoPlayer (DASH streams, etc) support
18+
* [ ] *Optional* ExoPlayer (DASH, HLS, SmoothStreaming, etc) support
1919
* [ ] *Optional* Caching for MediaPlayer
2020
* [ ] *Optional* Caching for ExoPlayer
2121
* [ ] *Optional* Chromecast support (Default/Styled Media Receiver)

Video.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const React = require('react');
2+
const ReactNative = require('react-native');
3+
4+
// TODO: make video work
5+
class Video extends React.Component {
6+
render() {
7+
return <NativeVideo {...this.props} />;
8+
}
9+
}
10+
11+
Video.propTypes = {
12+
player: React.PropTypes.number
13+
};
14+
15+
const NativeVideo = ReactNative.requireNativeComponent('TrackPlayerView', Video);
16+
17+
module.exports = Video;

android/build.gradle

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@ repositories {
3636
dependencies {
3737
//noinspection GradleDynamicVersion
3838
compile 'com.facebook.react:react-native:+'
39-
//compile 'com.google.android.exoplayer:exoplayer:r2.2.0'
39+
40+
provided 'com.danikula:videocache:2.6.4' // Caching for MediaPlayer
41+
provided 'com.google.android.exoplayer:exoplayer:r2.3.1' // ExoPlayer
42+
provided 'com.google.android.gms:play-services-cast-framework:10.2.1' // Chromecast
4043
}

android/src/main/java/guichaguri/trackplayer/TrackModule.java

+19-33
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@
88
import android.support.v4.media.RatingCompat;
99
import android.support.v4.media.session.PlaybackStateCompat;
1010
import com.facebook.react.bridge.Callback;
11-
import com.facebook.react.bridge.LifecycleEventListener;
1211
import com.facebook.react.bridge.ReactApplicationContext;
1312
import com.facebook.react.bridge.ReactContextBaseJavaModule;
1413
import com.facebook.react.bridge.ReactMethod;
1514
import com.facebook.react.bridge.ReadableMap;
1615
import guichaguri.trackplayer.logic.PlayerService;
1716
import guichaguri.trackplayer.logic.components.MediaWrapper;
1817
import java.io.IOException;
18+
import java.util.Arrays;
1919
import java.util.HashMap;
2020
import java.util.Map;
2121
import javax.annotation.Nullable;
2222

2323
/**
2424
* @author Guilherme Chaguri
2525
*/
26-
public class TrackModule extends ReactContextBaseJavaModule implements ServiceConnection, LifecycleEventListener {
26+
public class TrackModule extends ReactContextBaseJavaModule implements ServiceConnection {
2727

2828
private MediaWrapper manager = null;
29-
private Callback initCallback = null;
29+
private Callback[] initCallbacks = null;
3030

3131
public TrackModule(ReactApplicationContext context) {
3232
super(context);
@@ -43,46 +43,25 @@ public void initialize() {
4343

4444
ReactApplicationContext context = getReactApplicationContext();
4545

46-
context.addLifecycleEventListener(this);
47-
4846
Intent intent = new Intent(context, PlayerService.class);
4947
intent.setAction(PlayerService.ACTION_MEDIA);
5048
context.bindService(intent, this, Service.BIND_AUTO_CREATE);
5149
}
5250

5351
@Override
5452
public void onCatalystInstanceDestroy() {
55-
ReactApplicationContext context = getReactApplicationContext();
56-
57-
context.removeLifecycleEventListener(this);
58-
context.unbindService(this);
59-
}
60-
61-
@Override
62-
public void onHostResume() {
63-
64-
}
65-
66-
@Override
67-
public void onHostPause() {
68-
69-
}
70-
71-
@Override
72-
public void onHostDestroy() {
73-
ReactApplicationContext context = getReactApplicationContext();
74-
75-
context.removeLifecycleEventListener(this);
76-
context.unbindService(this);
53+
getReactApplicationContext().unbindService(this);
7754
}
7855

7956
@Override
8057
public void onServiceConnected(ComponentName name, IBinder service) {
8158
manager = (MediaWrapper)service;
8259

83-
if(initCallback != null) {
84-
initCallback.invoke();
85-
initCallback = null;
60+
if(initCallbacks != null) {
61+
for(Callback cb : initCallbacks) {
62+
cb.invoke();
63+
}
64+
initCallbacks = null;
8665
}
8766
}
8867

@@ -91,6 +70,8 @@ public void onServiceDisconnected(ComponentName name) {
9170
manager = null;
9271
}
9372

73+
/* ****************************** API ****************************** */
74+
9475
@Nullable
9576
@Override
9677
public Map<String, Object> getConstants() {
@@ -123,14 +104,19 @@ public Map<String, Object> getConstants() {
123104
return constants;
124105
}
125106

126-
/* ****************************** Native Functions ****************************** */
127-
128107
@ReactMethod
129108
public void onReady(Callback callback) {
130109
if(manager != null) {
131110
callback.invoke();
111+
return;
112+
}
113+
114+
if(initCallbacks == null) {
115+
initCallbacks = new Callback[]{callback};
132116
} else {
133-
initCallback = callback;
117+
int index = initCallbacks.length;
118+
initCallbacks = Arrays.copyOf(initCallbacks, index + 1);
119+
initCallbacks[index] = callback;
134120
}
135121
}
136122

android/src/main/java/guichaguri/trackplayer/TrackPackage.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public List<Class<? extends JavaScriptModule>> createJSModules() {
2929
@Override
3030
public List<ViewManager> createViewManagers(ReactApplicationContext context) {
3131
List<ViewManager> views = new ArrayList<>();
32-
views.add(new VideoManager(context));
32+
// TODO make video work
33+
//views.add(new VideoManager(context));
3334
return views;
3435
}
3536
}

android/src/main/java/guichaguri/trackplayer/VideoManager.java

+18-39
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,77 @@
55
import android.content.Intent;
66
import android.content.ServiceConnection;
77
import android.os.IBinder;
8-
import com.facebook.react.bridge.LifecycleEventListener;
98
import com.facebook.react.bridge.ReactApplicationContext;
109
import com.facebook.react.uimanager.SimpleViewManager;
1110
import com.facebook.react.uimanager.ThemedReactContext;
1211
import com.facebook.react.uimanager.annotations.ReactProp;
1312
import guichaguri.trackplayer.logic.PlayerService;
1413
import guichaguri.trackplayer.logic.components.VideoWrapper;
15-
import guichaguri.trackplayer.player.view.PlayerView;
14+
import guichaguri.trackplayer.player.components.PlayerView;
15+
import java.util.ArrayList;
16+
import java.util.List;
1617

1718
/**
1819
* @author Guilherme Chaguri
1920
*/
20-
public class VideoManager extends SimpleViewManager<PlayerView> implements ServiceConnection, LifecycleEventListener {
21+
public class VideoManager extends SimpleViewManager<PlayerView> implements ServiceConnection {
2122

2223
private final ReactApplicationContext context;
2324

2425
private VideoWrapper video;
25-
private int boundPlayer = -1;
26+
private List<PlayerView> views = new ArrayList<>();
2627

2728
public VideoManager(ReactApplicationContext context) {
2829
this.context = context;
2930
}
3031

3132
@Override
3233
public String getName() {
33-
return "PlayerView";
34+
return "TrackPlayerView";
3435
}
3536

3637
@Override
3738
public void initialize() {
3839
super.initialize();
3940

40-
context.addLifecycleEventListener(this);
41-
4241
Intent intent = new Intent(context, PlayerService.class);
4342
intent.setAction(PlayerService.ACTION_VIDEO);
4443
context.bindService(intent, this, Service.BIND_AUTO_CREATE);
4544
}
4645

4746
@Override
4847
public void onCatalystInstanceDestroy() {
49-
context.removeLifecycleEventListener(this);
5048
context.unbindService(this);
5149
}
5250

5351
@Override
5452
protected PlayerView createViewInstance(ThemedReactContext context) {
55-
return new PlayerView(context);
53+
PlayerView view = new PlayerView(context);
54+
views.add(view);
55+
return view;
5656
}
5757

5858
@Override
5959
public void onDropViewInstance(PlayerView view) {
6060
// Unbind the player
61-
if(boundPlayer != -1) {
62-
video.setView(boundPlayer, null);
63-
}
61+
view.bindPlayer(video, -1);
62+
views.remove(view);
6463
}
6564

6665
@ReactProp(name = "player", defaultInt = -1)
6766
public void setPlayer(PlayerView view, int id) {
68-
// Unbind the old player
69-
if(boundPlayer != -1) {
70-
video.setView(boundPlayer, null);
71-
}
72-
73-
// Bind the new player
74-
if(id != -1) {
75-
video.setView(id, view);
76-
}
77-
78-
boundPlayer = id;
79-
}
80-
81-
@Override
82-
public void onHostResume() {
83-
84-
}
85-
86-
@Override
87-
public void onHostPause() {
88-
89-
}
90-
91-
@Override
92-
public void onHostDestroy() {
93-
context.removeLifecycleEventListener(this);
94-
context.unbindService(this);
67+
// Bind the player
68+
view.bindPlayer(video, id);
9569
}
9670

9771
@Override
9872
public void onServiceConnected(ComponentName name, IBinder service) {
9973
video = (VideoWrapper)service;
74+
75+
// Update the views
76+
for(PlayerView view : views) {
77+
view.updatePlayer(video);
78+
}
10079
}
10180

10281
@Override

android/src/main/java/guichaguri/trackplayer/logic/MediaManager.java

+16-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import guichaguri.trackplayer.player.Player;
99
import guichaguri.trackplayer.player.RemotePlayer;
1010
import guichaguri.trackplayer.player.players.AndroidPlayer;
11-
import java.util.Arrays;
11+
import java.util.Collection;
12+
import java.util.HashMap;
13+
import java.util.Map;
1214

1315
/**
1416
* @author Guilherme Chaguri
@@ -18,8 +20,9 @@ public class MediaManager {
1820
private final PlayerService service;
1921
private final FocusManager focus;
2022
private final Metadata metadata;
21-
private Player[] players = new Player[0];
23+
private final Map<Integer, Player> players = new HashMap<>();
2224

25+
private int lastId = 0;
2326
private Player mainPlayer;
2427

2528
public MediaManager(PlayerService service) {
@@ -43,39 +46,30 @@ public void resetMetadata() {
4346
}
4447

4548
public int createPlayer() {
46-
int id = players.length;
47-
players = Arrays.copyOf(players, id + 1);
48-
players[id] = new AndroidPlayer(service, this); // TODO type
49+
int id = lastId++;
50+
players.put(id, new AndroidPlayer(service, this)); // TODO type
4951
return id;
5052
}
5153

5254
public void destroyPlayer(int id) {
5355
if(id == -1) {
5456
// Destroys all players
55-
for(Player p : players) p.destroy();
56-
players = new Player[0];
57+
for(Player p : players.values()) p.destroy();
58+
players.clear();
5759
} else {
58-
Player[] pls = new Player[players.length - 1];
59-
for(int o = 0; o < players.length; o++) {
60-
if(id == o) {
61-
players[o].destroy();
62-
} else {
63-
pls[o > id ? o - 1 : o] = players[o];
64-
}
65-
}
66-
players = pls;
60+
players.remove(id).destroy();
6761
}
6862
}
6963

7064
public Player getPlayer(int id) {
71-
if(id < 0 || id >= players.length) {
65+
if(id < 0 || !players.containsKey(id)) {
7266
throw new IllegalArgumentException();
7367
}
74-
return players[id];
68+
return players.get(id);
7569
}
7670

77-
public Player[] getPlayers() {
78-
return players;
71+
public Collection<Player> getPlayers() {
72+
return players.values();
7973
}
8074

8175
public void setMainPlayer(Player player) {
@@ -118,7 +112,7 @@ public void onCommand(Intent intent) {
118112
}
119113

120114
public void onServiceDestroy() {
121-
for(Player player : players) {
115+
for(Player player : getPlayers()) {
122116
player.destroy();
123117
}
124118
metadata.destroy();
@@ -167,7 +161,7 @@ private void onMainPlayerStop() {
167161
}
168162

169163
private boolean isPlayingLocal() {
170-
for(Player p : players) {
164+
for(Player p : getPlayers()) {
171165
if(p instanceof RemotePlayer) continue;
172166
if(Utils.isPlaying(p.getState())) return true;
173167
}

android/src/main/java/guichaguri/trackplayer/logic/components/VideoWrapper.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import android.os.Binder;
44
import guichaguri.trackplayer.logic.MediaManager;
5-
import guichaguri.trackplayer.player.view.PlayerView;
5+
import guichaguri.trackplayer.player.components.PlayerView;
66

77
/**
8+
* A wrapper of {@link MediaManager} to be used as a lightweight {@link android.os.IBinder} for binding views to players
89
* @author Guilherme Chaguri
910
*/
1011
public class VideoWrapper extends Binder {

0 commit comments

Comments
 (0)