diff --git a/Example/package.json b/Example/package.json index 9fc7124..72df776 100644 --- a/Example/package.json +++ b/Example/package.json @@ -3,6 +3,8 @@ "version": "0.0.1", "private": true, "scripts": { + "reinstall": "yarn purge-modules && yarn", + "purge-modules":"rm -rf node_modules", "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", diff --git a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java index f3da159..18e58f3 100644 --- a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +++ b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java @@ -9,7 +9,6 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.IllegalViewOperationException; @@ -18,570 +17,573 @@ import com.facebook.react.uimanager.UIManagerModule; import com.jwplayer.pub.api.JWPlayer; import com.jwplayer.pub.api.PlayerState; -import com.jwplayer.pub.api.media.adaptive.QualityLevel; import com.jwplayer.pub.api.configuration.PlayerConfig; +import com.jwplayer.pub.api.media.adaptive.QualityLevel; import com.jwplayer.pub.api.media.audio.AudioTrack; -import com.jwplayer.pub.api.media.playlists.PlaylistItem; -import java.util.ArrayList; import java.util.List; public class RNJWPlayerModule extends ReactContextBaseJavaModule { - private final ReactApplicationContext mReactContext; - - private static final String TAG = "RNJWPlayerModule"; - - public RNJWPlayerModule(ReactApplicationContext reactContext) { - super(reactContext); - - mReactContext = reactContext; - } - - @Override - public String getName() { - return TAG; - } - - @ReactMethod - public void loadPlaylist(final int reactTag, final ReadableArray playlistItems) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - JWPlayer player = playerView.mPlayerView.getPlayer(); - - PlayerConfig oldConfig = player.getConfig(); - PlayerConfig config = new PlayerConfig.Builder() - .autostart(oldConfig.getAutostart()) - .nextUpOffset(oldConfig.getNextUpOffset()) - .repeat(oldConfig.getRepeat()) - .relatedConfig(oldConfig.getRelatedConfig()) - .displayDescription(oldConfig.getDisplayDescription()) - .displayTitle(oldConfig.getDisplayTitle()) - .advertisingConfig(oldConfig.getAdvertisingConfig()) - .stretching(oldConfig.getStretching()) - .uiConfig(oldConfig.getUiConfig()) - .playlist(Util.createPlaylist(playlistItems)) - .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects()) - .preload(oldConfig.getPreload()) - .useTextureView(oldConfig.useTextureView()) - .thumbnailPreview(oldConfig.getThumbnailPreview()) - .mute(oldConfig.getMute()) - .build(); - - player.setup(config); - } + private final ReactApplicationContext mReactContext; + + private static final String TAG = "RNJWPlayerModule"; + + public RNJWPlayerModule(ReactApplicationContext reactContext) { + super(reactContext); + + mReactContext = reactContext; + } + + @Override + public String getName() { + return TAG; + } + + @ReactMethod + public void loadPlaylist(final int reactTag, final ReadableArray playlistItems) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + JWPlayer player = playerView.mPlayerView.getPlayer(); + + PlayerConfig oldConfig = player.getConfig(); + PlayerConfig config = new PlayerConfig.Builder() + .autostart(oldConfig.getAutostart()) + .nextUpOffset(oldConfig.getNextUpOffset()) + .repeat(oldConfig.getRepeat()) + .relatedConfig(oldConfig.getRelatedConfig()) + .displayDescription(oldConfig.getDisplayDescription()) + .displayTitle(oldConfig.getDisplayTitle()) + .advertisingConfig(oldConfig.getAdvertisingConfig()) + .stretching(oldConfig.getStretching()) + .uiConfig(oldConfig.getUiConfig()) + .playlist(Util.createPlaylist(playlistItems)) + .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects()) + .preload(oldConfig.getPreload()) + .useTextureView(oldConfig.useTextureView()) + .thumbnailPreview(oldConfig.getThumbnailPreview()) + .mute(oldConfig.getMute()) + .build(); + + player.setup(config); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void loadPlaylist(final int reactTag, final String playlistUrl) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - JWPlayer player = playerView.mPlayerView.getPlayer(); - - PlayerConfig oldConfig = player.getConfig(); - PlayerConfig config = new PlayerConfig.Builder() - .autostart(oldConfig.getAutostart()) - .nextUpOffset(oldConfig.getNextUpOffset()) - .repeat(oldConfig.getRepeat()) - .relatedConfig(oldConfig.getRelatedConfig()) - .displayDescription(oldConfig.getDisplayDescription()) - .displayTitle(oldConfig.getDisplayTitle()) - .advertisingConfig(oldConfig.getAdvertisingConfig()) - .stretching(oldConfig.getStretching()) - .uiConfig(oldConfig.getUiConfig()) - .playlistUrl(playlistUrl) - .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects()) - .preload(oldConfig.getPreload()) - .useTextureView(oldConfig.useTextureView()) - .thumbnailPreview(oldConfig.getThumbnailPreview()) - .mute(oldConfig.getMute()) - .build(); - - player.setup(config); - } + + @ReactMethod + public void loadPlaylist(final int reactTag, final String playlistUrl) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute (NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + JWPlayer player = playerView.mPlayerView.getPlayer(); + + PlayerConfig oldConfig = player.getConfig(); + PlayerConfig config = new PlayerConfig.Builder() + .autostart(oldConfig.getAutostart()) + .nextUpOffset(oldConfig.getNextUpOffset()) + .repeat(oldConfig.getRepeat()) + .relatedConfig(oldConfig.getRelatedConfig()) + .displayDescription(oldConfig.getDisplayDescription()) + .displayTitle(oldConfig.getDisplayTitle()) + .advertisingConfig(oldConfig.getAdvertisingConfig()) + .stretching(oldConfig.getStretching()) + .uiConfig(oldConfig.getUiConfig()) + .playlistUrl(playlistUrl) + .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects()) + .preload(oldConfig.getPreload()) + .useTextureView(oldConfig.useTextureView()) + .thumbnailPreview(oldConfig.getThumbnailPreview()) + .mute(oldConfig.getMute()) + .build(); + + player.setup(config); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void play(final int reactTag) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().play(); - } + + @ReactMethod + public void play(final int reactTag) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().play(); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void toggleSpeed(final int reactTag) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - double rate = playerView.mPlayerView.getPlayer().getPlaybackRate(); - if (rate < 2) { - playerView.mPlayerView.getPlayer().setPlaybackRate(rate += 0.5); - } else { - playerView.mPlayerView.getPlayer().setPlaybackRate((float) 0.5); - } - } + + @ReactMethod + public void toggleSpeed(final int reactTag) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + double rate = playerView.mPlayerView.getPlayer().getPlaybackRate(); + if (rate < 2) { + playerView.mPlayerView.getPlayer().setPlaybackRate(rate += 0.5); + } else { + playerView.mPlayerView.getPlayer().setPlaybackRate((float) 0.5); + } + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void togglePIP(final int reactTag) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - if (playerView.mPlayerView.getPlayer().isInPictureInPictureMode()) { - playerView.mPlayerView.getPlayer().exitPictureInPictureMode(); - } else { - playerView.mPlayerView.getPlayer().enterPictureInPictureMode(); - } - } + + @ReactMethod + public void togglePIP(final int reactTag) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + if (playerView.mPlayerView.getPlayer().isInPictureInPictureMode()) { + playerView.mPlayerView.getPlayer().exitPictureInPictureMode(); + } else { + playerView.mPlayerView.getPlayer().enterPictureInPictureMode(); + } + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setSpeed(final int reactTag, final float speed) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().setPlaybackRate(speed); - } + + @ReactMethod + public void setSpeed(final int reactTag, final float speed) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().setPlaybackRate(speed); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - - @ReactMethod - public void getCurrentQuality(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - int quality = playerView.mPlayerView.getPlayer().getCurrentQuality(); - promise.resolve(quality); - } else { - promise.reject("RNJW Error", "getCurrentQuality() Player is null"); - } + + + @ReactMethod + public void getCurrentQuality(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + int quality = playerView.mPlayerView.getPlayer().getCurrentQuality(); + promise.resolve(quality); + } else { + promise.reject("RNJW Error", "getCurrentQuality() Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setCurrentQuality(final int reactTag, final int index) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().setCurrentQuality(index); - } + + @ReactMethod + public void setCurrentQuality(final int reactTag, final int index) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().setCurrentQuality(index); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - - @ReactMethod - public void getQualityLevels(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - List qualityLevelsList = playerView.mPlayerView.getPlayer().getQualityLevels(); - if (qualityLevelsList == null) { //if qualitylevels are null than pass empty array. - promise.resolve(null); - return; - } - WritableArray qualityLevels = Arguments.createArray(); - for (int i = 0; i < qualityLevelsList.size(); i++) { - WritableMap qualityLevel = Arguments.createMap(); - QualityLevel level = qualityLevelsList.get(i); - qualityLevel.putInt("playListPosition", level.getPlaylistPosition()); - qualityLevel.putInt("bitRate", level.getBitrate()); - qualityLevel.putString("label", level.getLabel()); - qualityLevel.putInt("height", level.getHeight()); - qualityLevel.putInt("width", level.getWidth()); - qualityLevel.putInt("index", level.getTrackIndex()); - qualityLevels.pushMap(qualityLevel); - } - promise.resolve(qualityLevels); - } else { - promise.reject("RNJW Error", "getQualityLevels() Player is null"); - } + + + @ReactMethod + public void getQualityLevels(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + List qualityLevelsList = playerView.mPlayerView.getPlayer().getQualityLevels(); + if (qualityLevelsList == null) { //if qualitylevels are null than pass empty array. + promise.resolve(null); + return; + } + WritableArray qualityLevels = Arguments.createArray(); + for (int i = 0; i < qualityLevelsList.size(); i++) { + WritableMap qualityLevel = Arguments.createMap(); + QualityLevel level = qualityLevelsList.get(i); + qualityLevel.putInt("playListPosition", level.getPlaylistPosition()); + qualityLevel.putInt("bitRate", level.getBitrate()); + qualityLevel.putString("label", level.getLabel()); + qualityLevel.putInt("height", level.getHeight()); + qualityLevel.putInt("width", level.getWidth()); + qualityLevel.putInt("index", level.getTrackIndex()); + qualityLevels.pushMap(qualityLevel); + } + promise.resolve(qualityLevels); + } else { + promise.reject("RNJW Error", "getQualityLevels() Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void pause(final int reactTag) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().pause(); - playerView.userPaused = true; - } + + @ReactMethod + public void pause(final int reactTag) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + if (!playerView.getIsCastActive()) { + playerView.mPlayerView.getPlayer().pause(); + playerView.userPaused = true; + + } + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void stop(final int reactTag) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().stop(); - playerView.userPaused = true; - } + + @ReactMethod + public void stop(final int reactTag) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + if (!playerView.getIsCastActive()) { + playerView.mPlayerView.getPlayer().stop(); + playerView.userPaused = true; + } + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void seekTo(final int reactTag, final double time) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().seek(time); - } + + @ReactMethod + public void seekTo(final int reactTag, final double time) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().seek(time); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setPlaylistIndex(final int reactTag, final int index) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().playlistItem(index); - } + + @ReactMethod + public void setPlaylistIndex(final int reactTag, final int index) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().playlistItem(index); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setControls(final int reactTag, final boolean show) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().setControls(show); - } + + @ReactMethod + public void setControls(final int reactTag, final boolean show) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().setControls(show); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void position(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - promise.resolve((Double.valueOf(playerView.mPlayerView.getPlayer().getPosition()).intValue())); - } else { - promise.reject("RNJW Error", "Player is null"); - } + + @ReactMethod + public void position(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + promise.resolve((Double.valueOf(playerView.mPlayerView.getPlayer().getPosition()).intValue())); + } else { + promise.reject("RNJW Error", "Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + promise.reject("RNJW Error", e); } - }); - } catch (IllegalViewOperationException e) { - promise.reject("RNJW Error", e); } - } - - @ReactMethod - public void state(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - PlayerState playerState = playerView.mPlayerView.getPlayer().getState(); - promise.resolve(stateToInt(playerState)); - } else { - promise.reject("RNJW Error", "Player is null"); - } + + @ReactMethod + public void state(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + PlayerState playerState = playerView.mPlayerView.getPlayer().getState(); + promise.resolve(stateToInt(playerState)); + } else { + promise.reject("RNJW Error", "Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + promise.reject("RNJW Error", e); } - }); - } catch (IllegalViewOperationException e) { - promise.reject("RNJW Error", e); } - } - - @ReactMethod - public void setFullscreen(final int reactTag, final boolean fullscreen) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - playerView.mPlayerView.getPlayer().setFullscreen(fullscreen, fullscreen); - } + + @ReactMethod + public void setFullscreen(final int reactTag, final boolean fullscreen) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + playerView.mPlayerView.getPlayer().setFullscreen(fullscreen, fullscreen); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setVolume(final int reactTag, final int volume) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayerView != null) { - int maxValue = playerView.audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - if (volume <= maxValue) { - playerView.audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); - } else { - playerView.audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxValue, 0); - } - } + + @ReactMethod + public void setVolume(final int reactTag, final int volume) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayerView != null) { + int maxValue = playerView.audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + if (volume <= maxValue) { + playerView.audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } else { + playerView.audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxValue, 0); + } + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void getAudioTracks(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayer != null) { - List audioTrackList = playerView.mPlayer.getAudioTracks(); - WritableArray audioTracks = Arguments.createArray(); - if (audioTrackList != null) { - for (int i = 0; i < audioTrackList.size(); i++) { - WritableMap audioTrack = Arguments.createMap(); - AudioTrack track = audioTrackList.get(i); - audioTrack.putString("name", track.getName()); - audioTrack.putString("language", track.getLanguage()); - audioTrack.putString("groupId", track.getGroupId()); - audioTrack.putBoolean("defaultTrack", track.isDefaultTrack()); - audioTrack.putBoolean("autoSelect", track.isAutoSelect()); - audioTracks.pushMap(audioTrack); - } - } - promise.resolve(audioTracks); - } else { - promise.reject("RNJW Error", "Player is null"); - } + + @ReactMethod + public void getAudioTracks(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayer != null) { + List audioTrackList = playerView.mPlayer.getAudioTracks(); + WritableArray audioTracks = Arguments.createArray(); + if (audioTrackList != null) { + for (int i = 0; i < audioTrackList.size(); i++) { + WritableMap audioTrack = Arguments.createMap(); + AudioTrack track = audioTrackList.get(i); + audioTrack.putString("name", track.getName()); + audioTrack.putString("language", track.getLanguage()); + audioTrack.putString("groupId", track.getGroupId()); + audioTrack.putBoolean("defaultTrack", track.isDefaultTrack()); + audioTrack.putBoolean("autoSelect", track.isAutoSelect()); + audioTracks.pushMap(audioTrack); + } + } + promise.resolve(audioTracks); + } else { + promise.reject("RNJW Error", "Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + promise.reject("RNJW Error", e); } - }); - } catch (IllegalViewOperationException e) { - promise.reject("RNJW Error", e); } - } - - @ReactMethod - public void getCurrentAudioTrack(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayer != null) { - promise.resolve(playerView.mPlayer.getCurrentAudioTrack()); - } else { - promise.reject("RNJW Error", "Player is null"); - } + + @ReactMethod + public void getCurrentAudioTrack(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayer != null) { + promise.resolve(playerView.mPlayer.getCurrentAudioTrack()); + } else { + promise.reject("RNJW Error", "Player is null"); + } + } + }); + } catch (IllegalViewOperationException e) { + promise.reject("RNJW Error", e); } - }); - } catch (IllegalViewOperationException e) { - promise.reject("RNJW Error", e); } - } - - @ReactMethod - public void setCurrentAudioTrack(final int reactTag, final int index) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayer != null) { - playerView.mPlayer.setCurrentAudioTrack(index); - } + + @ReactMethod + public void setCurrentAudioTrack(final int reactTag, final int index) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayer != null) { + playerView.mPlayer.setCurrentAudioTrack(index); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - - @ReactMethod - public void setCurrentCaptions(final int reactTag, final int index) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - - if (playerView != null && playerView.mPlayer != null) { - playerView.mPlayer.setCurrentCaptions(index); - } + + @ReactMethod + public void setCurrentCaptions(final int reactTag, final int index) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute(NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + + if (playerView != null && playerView.mPlayer != null) { + playerView.mPlayer.setCurrentCaptions(index); + } + } + }); + } catch (IllegalViewOperationException e) { + throw e; } - }); - } catch (IllegalViewOperationException e) { - throw e; } - } - @ReactMethod - public void getCurrentCaptions(final int reactTag, final Promise promise) { - try { - UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); - uiManager.addUIBlock(new UIBlock() { - public void execute (NativeViewHierarchyManager nvhm) { - RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); + @ReactMethod + public void getCurrentCaptions(final int reactTag, final Promise promise) { + try { + UIManagerModule uiManager = mReactContext.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(new UIBlock() { + public void execute (NativeViewHierarchyManager nvhm) { + RNJWPlayerView playerView = (RNJWPlayerView) nvhm.resolveView(reactTag); - if (playerView != null && playerView.mPlayer != null) { - promise.resolve(playerView.mPlayer.getCurrentCaptions()); - } else { - promise.reject("RNJW Error", "Player is null"); + if (playerView != null && playerView.mPlayer != null) { + promise.resolve(playerView.mPlayer.getCurrentCaptions()); + } else { + promise.reject("RNJW Error", "Player is null"); + } } - } - }); - } catch (IllegalViewOperationException e) { - throw e; - } - } - - - private int stateToInt(PlayerState playerState) { - switch (playerState) { - case IDLE: - return 0; - case BUFFERING: - return 1; - case PLAYING: - return 2; - case PAUSED: - return 3; - case COMPLETE: - return 4; - case ERROR: - return 5; - default: - return -1; + }); + } catch (IllegalViewOperationException e) { + throw e; + } + } + + + private int stateToInt(PlayerState playerState) { + switch (playerState) { + case IDLE: + return 0; + case BUFFERING: + return 1; + case PLAYING: + return 2; + case PAUSED: + return 3; + case COMPLETE: + return 4; + case ERROR: + return 5; + default: + return -1; + } } - } } \ No newline at end of file diff --git a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java index 7bb0f31..3ad297e 100755 --- a/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +++ b/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java @@ -2,6 +2,7 @@ import android.app.Activity; +import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -25,7 +26,13 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; import com.facebook.react.ReactActivity; import com.facebook.react.bridge.Arguments; @@ -43,6 +50,7 @@ import com.jwplayer.pub.api.JWPlayer; import com.jwplayer.pub.api.JsonHelper; import com.jwplayer.pub.api.UiGroup; +import com.jwplayer.pub.api.background.MediaService; import com.jwplayer.pub.api.background.MediaServiceController; import com.jwplayer.pub.api.configuration.PlayerConfig; import com.jwplayer.pub.api.configuration.UiConfig; @@ -179,7 +187,7 @@ public class RNJWPlayerView extends RelativeLayout implements AudioManager.OnAudioFocusChangeListener, - LifecycleEventListener { + LifecycleEventListener, LifecycleOwner { public RNJWPlayer mPlayerView = null; public JWPlayer mPlayer = null; @@ -230,7 +238,13 @@ public class RNJWPlayerView extends RelativeLayout implements private void doBindService() { if (mMediaServiceController != null) { - mMediaServiceController.bindService(); + if (!isBackgroundAudioServiceRunning()) { + // This may not be your expected behavior, but is necessary to avoid crashing + // Do not use multiple player instances with background audio enabled + + // don't rebind me if the service is already active with a player. + mMediaServiceController.bindService(); + } } } @@ -265,10 +279,23 @@ private static Context getNonBuggyContext(ThemedReactContext reactContext, return superContext; } + private boolean isBackgroundAudioServiceRunning() { + ActivityManager manager = (ActivityManager) mAppContext.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (MediaService.class.getName().equals(service.service.getClassName())) { + Log.w(TAG, "MediaService is already running with another player loaded. To avoid crashing, this player, " + + mPlayerView.getTag() + " will not be loaded into the background service."); + return true; + } + } + return false; + } + public RNJWPlayerView(ThemedReactContext reactContext, ReactApplicationContext appContext) { super(getNonBuggyContext(reactContext, appContext)); mAppContext = appContext; + registry.setCurrentState(Lifecycle.State.CREATED); mThemedReactContext = reactContext; mActivity = (ReactActivity) getActivity(); @@ -276,11 +303,25 @@ public RNJWPlayerView(ThemedReactContext reactContext, ReactApplicationContext a mWindow = mActivity.getWindow(); } + if (mActivity != null) { + mActivity.getLifecycle().addObserver(lifecycleObserver); + } + mRootView = mActivity.findViewById(android.R.id.content); getReactContext().addLifecycleEventListener(this); } + private LifecycleObserver lifecycleObserver = new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event.getTargetState() == Lifecycle.State.DESTROYED) { + return; // no op: handled elsewhere + } + registry.setCurrentState(event.getTargetState()); + } + }; + public ReactApplicationContext getAppContext() { return mAppContext; } @@ -303,11 +344,37 @@ public Activity getActivity() { return mThemedReactContext.getReactApplicationContext().getCurrentActivity(); } + // The registry for lifecycle events. Required by player object. Main use case if for garbage collection / teardown + private final LifecycleRegistry registry = new LifecycleRegistry(this); + + @NonNull + @Override + public Lifecycle getLifecycle() { + return registry; + } + + // // closest to `ondestroy` for a view without listening to the activity event + // // The activity event can be deceptive in React-Native + // @Override + // protected void onDetachedFromWindow() { + // super.onDetachedFromWindow(); + // registry.setCurrentState(Lifecycle.State.DESTROYED); + // } + public void destroyPlayer() { if (mPlayer != null) { unRegisterReceiver(); + + // Only call stop if we are not casting as stop will break the current cast session + if (!mIsCastActive) { + mPlayer.stop(); + } + // send signal to JW SDK player is destroyed + registry.setCurrentState(Lifecycle.State.DESTROYED); + + // Stop listening to activities lifecycle + mActivity.getLifecycle().removeObserver(lifecycleObserver); mPlayer.deregisterActivityForPip(); - mPlayer.stop(); mPlayer.removeListeners(this, // VideoPlayerEvents @@ -456,6 +523,7 @@ public void setupPlayerView(Boolean backgroundAudioEnabled) { /** * Helper to build the a generic `ExtensibleFullscreenHandler` with small tweaks to play nice with Modals + * * @return {@link ExtensibleFullscreenHandler} */ private ExtensibleFullscreenHandler createModalFullscreenHandler() { @@ -974,6 +1042,11 @@ private void setupPlayer(ReadableMap prop) { LinearLayout.LayoutParams.MATCH_PARENT)); addView(mPlayerView); + // Ensure we have a valid state before applying to the player + registry.setCurrentState(Lifecycle.State.STARTED); + + mPlayer = mPlayerView.getPlayer(this); + if (prop.hasKey("controls")) { // Hack for controls hiding not working right away mPlayerView.getPlayer().setControls(prop.getBoolean("controls")); } @@ -1000,8 +1073,6 @@ private void setupPlayer(ReadableMap prop) { mPlayerView.exitFullScreenOnPortrait = exitFullScreenOnPortrait; } - mPlayer = mPlayerView.getPlayer(); - if (isJwConfig) { mPlayer.setup(jwConfig); } else { @@ -1080,8 +1151,6 @@ private void setupPlayer(ReadableMap prop) { if (backgroundAudioEnabled) { audioManager = (AudioManager) simpleContext.getSystemService(Context.AUDIO_SERVICE); - // Throws a fatal error if using a playlistURL instead of manually created playlist - // Related to SDK-11346 mMediaServiceController = new MediaServiceController.Builder((AppCompatActivity) mActivity, mPlayer) .build(); } @@ -1711,6 +1780,17 @@ public void onPipOpen(PipOpenEvent pipOpenEvent) { // Casting events + private boolean mIsCastActive = false; + + /** + * Get if this player-view is currently casting + * + * @return true if casting + */ + public boolean getIsCastActive() { + return mIsCastActive; + } + @Override public void onCast(CastEvent castEvent) { WritableMap event = Arguments.createMap(); @@ -1719,6 +1799,7 @@ public void onCast(CastEvent castEvent) { event.putBoolean("active", castEvent.isActive()); event.putBoolean("available", castEvent.isAvailable()); getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCasting", event); + mIsCastActive = castEvent.isActive(); // stop/start the background audio service if it's running and we're casting if (castEvent.isActive()) { doUnbindService();