diff --git a/API.md b/API.md index be1d021f..7b5f099b 100644 --- a/API.md +++ b/API.md @@ -1645,6 +1645,30 @@ this.player.seek(120, 50); // Seek to 2 minutes with +/- 50 milliseconds accurac Platforms: iOS +### pause +`pause(): Promise` + +Pause the video. + +Example: +``` +this.player.pause(); +``` + +Platforms: Android, iOS + +### play +`play(): Promise` + +Play the video. + +Example: +``` +this.player.play(); +``` + +Platforms: Android, iOS + #### Static methods ### Video Decoding capabilities diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc755f4..d87c2de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Changelog ## Next +- iOS, Android: expose playback functions to ref [#3245](https://github.com/react-native-video/react-native-video/pull/3245) - Windows: fix build error from over-specified SDK version [#3246](https://github.com/react-native-video/react-native-video/pull/3246) - Windows: fix `onError` not being raised [#3247](https://github.com/react-native-video/react-native-video/pull/3247) - **BREAKING CHANGE**❗️Android: update isCodecSupported to return enum [#3254](https://github.com/react-native-video/react-native-video/pull/3254) diff --git a/Video.js b/Video.js index 3d342d02..44616b54 100644 --- a/Video.js +++ b/Video.js @@ -14,8 +14,8 @@ const styles = StyleSheet.create({ }, }); -const { VideoDecoderProperties } = NativeModules -export { TextTrackType, FilterType, DRMType, VideoDecoderProperties } +const { VideoDecoderProperties } = NativeModules; +export { TextTrackType, FilterType, DRMType, VideoDecoderProperties }; export default class Video extends Component { @@ -81,6 +81,18 @@ export default class Video extends Component { return await NativeModules.VideoManager.save(options, findNodeHandle(this._root)); } + pause = async () => { + await this.setPlayerPauseState(true); + }; + + play = async () => { + await this.setPlayerPauseState(false); + }; + + setPlayerPauseState = async (paused) => { + return await NativeModules.VideoManager.setPlayerPauseState(paused, findNodeHandle(this._root)); + }; + restoreUserInterfaceForPictureInPictureStopCompleted = (restored) => { this.setNativeProps({ restoreUserInterfaceForPIPStopCompletionHandler: restored }); }; diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 8ef2afca..d62b38fc 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -116,7 +116,7 @@ import java.util.concurrent.TimeUnit; import java.lang.Integer; @SuppressLint("ViewConstructor") -class ReactExoplayerView extends FrameLayout implements +public class ReactExoplayerView extends FrameLayout implements LifecycleEventListener, Player.Listener, BandwidthMeter.EventListener, diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java index 23bedad6..95afa512 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java @@ -9,6 +9,7 @@ import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -25,9 +26,12 @@ public class ReactVideoPackage implements ReactPackage { @Override public List createNativeModules(ReactApplicationContext reactContext) { - return Collections.singletonList( - new VideoDecoderPropertiesModule(reactContext) - ); + List modules = new ArrayList(); + + modules.add(new VideoDecoderPropertiesModule(reactContext)); + modules.add(new VideoManagerModule(reactContext)); + + return modules; } // Deprecated RN 0.47 diff --git a/android/src/main/java/com/brentvatne/react/VideoManagerModule.java b/android/src/main/java/com/brentvatne/react/VideoManagerModule.java new file mode 100644 index 00000000..69106a47 --- /dev/null +++ b/android/src/main/java/com/brentvatne/react/VideoManagerModule.java @@ -0,0 +1,39 @@ +package com.brentvatne.react; + +import android.view.View; + +import androidx.annotation.NonNull; + +import com.brentvatne.exoplayer.ReactExoplayerView; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.uimanager.UIManagerModule; + +public class VideoManagerModule extends ReactContextBaseJavaModule { + ReactApplicationContext reactContext; + + @NonNull + @Override + public String getName() { + return "VideoManager"; + } + + @ReactMethod + public void setPlayerPauseState(Boolean paused, int reactTag) { + UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class); + uiManager.prependUIBlock(manager -> { + View view = manager.resolveView(reactTag); + + if (view instanceof ReactExoplayerView) { + ReactExoplayerView videoView = (ReactExoplayerView) view; + videoView.setPausedModifier(paused); + } + }); + } + + public VideoManagerModule(ReactApplicationContext reactContext) { + super(reactContext); + this.reactContext = reactContext; + } +} \ No newline at end of file diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 9bdf6101..2810f142 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -74,6 +74,9 @@ RCT_EXTERN_METHOD(setLicenseResult:(NSString *)license RCT_EXTERN_METHOD(setLicenseResultError(NSString *)error reactTag:(nonnull NSNumber *)reactTag) +RCT_EXTERN_METHOD(setPlayerPauseState:(nonnull NSNumber *)paused + reactTag:(nonnull NSNumber *)reactTag) + RCT_EXTERN_METHOD(presentFullscreenPlayer reactTag:(nonnull NSNumber *)reactTag) diff --git a/ios/Video/RCTVideoManager.swift b/ios/Video/RCTVideoManager.swift index bfdec7a4..64e5eccd 100644 --- a/ios/Video/RCTVideoManager.swift +++ b/ios/Video/RCTVideoManager.swift @@ -71,6 +71,19 @@ class RCTVideoManager: RCTViewManager { }) } + @objc(setPlayerPauseState:reactTag:) + func setPlayerPauseState(paused: NSNumber, reactTag: NSNumber) -> Void { + bridge.uiManager.prependUIBlock({_ , viewRegistry in + let view = viewRegistry?[reactTag] + if !(view is RCTVideo) { + RCTLogError("Invalid view returned from registry, expecting RCTVideo, got: %@", String(describing: view)) + } else if let view = view as? RCTVideo { + let paused = paused.boolValue + view.setPaused(paused) + } + }) + } + override func constantsToExport() -> [AnyHashable : Any]? { return [ "ScaleNone": AVLayerVideoGravity.resizeAspect,