Merge branch 'master' into bugfix/android-disablefocus-audio
This commit is contained in:
commit
3d80bfd236
12
CHANGELOG.md
12
CHANGELOG.md
@ -2,7 +2,18 @@
|
|||||||
|
|
||||||
### next
|
### next
|
||||||
* Fix Android videos being able to play with background music/audio from other apps.
|
* Fix Android videos being able to play with background music/audio from other apps.
|
||||||
|
|
||||||
|
### Version 4.4.0
|
||||||
|
* Fix runtime warning by replacing `UIManager.RCTVideo` with `UIManager.getViewManagerConfig('RCTVideo')` (and ensuring backwards compat) [#1487](https://github.com/react-native-community/react-native-video/pull/1487)
|
||||||
* Fix loading package resolved videos when using video-caching [#1438](https://github.com/react-native-community/react-native-video/pull/1438)
|
* Fix loading package resolved videos when using video-caching [#1438](https://github.com/react-native-community/react-native-video/pull/1438)
|
||||||
|
* Fix "message sent to deallocated instance" crash on ios [#1482](https://github.com/react-native-community/react-native-video/pull/1482)
|
||||||
|
* Display a warning when source is empty [#1478](https://github.com/react-native-community/react-native-video/pull/1478)
|
||||||
|
* Don't crash on iOS for an empty source [#1246](https://github.com/react-native-community/react-native-video/pull/1246)
|
||||||
|
* Recover from from transient internet failures when loading on ExoPlayer [#1448](https://github.com/react-native-community/react-native-video/pull/1448)
|
||||||
|
* Add controls support for ExoPlayer [#1414](https://github.com/react-native-community/react-native-video/pull/1414)
|
||||||
|
* Fix check for text tracks when iOS caching enabled [#1387](https://github.com/react-native-community/react-native-video/pull/1387)
|
||||||
|
* Add support for Picture in Picture on iOS [#1325](https://github.com/react-native-community/react-native-video/pull/1325)
|
||||||
|
* Fix UIManager undefined variable [#1488](https://github.com/react-native-community/react-native-video/pull/1488)
|
||||||
|
|
||||||
### Version 4.3.0
|
### Version 4.3.0
|
||||||
* Fix iOS video not displaying after switching source [#1395](https://github.com/react-native-community/react-native-video/pull/1395)
|
* Fix iOS video not displaying after switching source [#1395](https://github.com/react-native-community/react-native-video/pull/1395)
|
||||||
@ -10,6 +21,7 @@
|
|||||||
* Fix text not appearing in release builds of Android apps [#1373](https://github.com/react-native-community/react-native-video/pull/1373)
|
* Fix text not appearing in release builds of Android apps [#1373](https://github.com/react-native-community/react-native-video/pull/1373)
|
||||||
* Update to ExoPlayer 2.9.3 [#1406](https://github.com/react-native-community/react-native-video/pull/1406)
|
* Update to ExoPlayer 2.9.3 [#1406](https://github.com/react-native-community/react-native-video/pull/1406)
|
||||||
* Add video track selection & onBandwidthUpdate [#1199](https://github.com/react-native-community/react-native-video/pull/1199)
|
* Add video track selection & onBandwidthUpdate [#1199](https://github.com/react-native-community/react-native-video/pull/1199)
|
||||||
|
* Recovery from transient internet failures and props to configure the custom retry count [#1448](https://github.com/react-native-community/react-native-video/pull/1448)
|
||||||
|
|
||||||
### Version 4.2.0
|
### Version 4.2.0
|
||||||
* Don't initialize filters on iOS unless a filter is set. This was causing a startup performance regression [#1360](https://github.com/react-native-community/react-native-video/pull/1360)
|
* Don't initialize filters on iOS unless a filter is set. This was causing a startup performance regression [#1360](https://github.com/react-native-community/react-native-video/pull/1360)
|
||||||
|
63
README.md
63
README.md
@ -269,8 +269,10 @@ var styles = StyleSheet.create({
|
|||||||
* [id](#id)
|
* [id](#id)
|
||||||
* [ignoreSilentSwitch](#ignoresilentswitch)
|
* [ignoreSilentSwitch](#ignoresilentswitch)
|
||||||
* [maxBitRate](#maxbitrate)
|
* [maxBitRate](#maxbitrate)
|
||||||
|
* [minLoadRetryCount](#minLoadRetryCount)
|
||||||
* [muted](#muted)
|
* [muted](#muted)
|
||||||
* [paused](#paused)
|
* [paused](#paused)
|
||||||
|
* [pictureInPicture](#pictureinpicture)
|
||||||
* [playInBackground](#playinbackground)
|
* [playInBackground](#playinbackground)
|
||||||
* [playWhenInactive](#playwheninactive)
|
* [playWhenInactive](#playwheninactive)
|
||||||
* [poster](#poster)
|
* [poster](#poster)
|
||||||
@ -300,14 +302,17 @@ var styles = StyleSheet.create({
|
|||||||
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
||||||
* [onLoad](#onload)
|
* [onLoad](#onload)
|
||||||
* [onLoadStart](#onloadstart)
|
* [onLoadStart](#onloadstart)
|
||||||
|
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
|
||||||
* [onProgress](#onprogress)
|
* [onProgress](#onprogress)
|
||||||
* [onSeek](#onseek)
|
* [onSeek](#onseek)
|
||||||
|
* [onRestoreUserInterfaceForPictureInPictureStop](#onrestoreuserinterfaceforpictureinpicturestop)
|
||||||
* [onTimedMetadata](#ontimedmetadata)
|
* [onTimedMetadata](#ontimedmetadata)
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
* [dismissFullscreenPlayer](#dismissfullscreenplayer)
|
* [dismissFullscreenPlayer](#dismissfullscreenplayer)
|
||||||
* [presentFullscreenPlayer](#presentfullscreenplayer)
|
* [presentFullscreenPlayer](#presentfullscreenplayer)
|
||||||
* [save](#save)
|
* [save](#save)
|
||||||
|
* [restoreUserInterfaceForPictureInPictureStop](#restoreuserinterfaceforpictureinpicturestop)
|
||||||
* [seek](#seek)
|
* [seek](#seek)
|
||||||
|
|
||||||
### Configurable props
|
### Configurable props
|
||||||
@ -359,9 +364,9 @@ Determines whether to show player controls.
|
|||||||
|
|
||||||
Note on iOS, controls are always shown when in fullscreen mode.
|
Note on iOS, controls are always shown when in fullscreen mode.
|
||||||
|
|
||||||
Controls are not available Android because the system does not provide a stock set of controls. You will need to build your own or use a package like [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls) or [react-native-video-player](https://github.com/cornedor/react-native-video-player).
|
For Android MediaPlayer, you will need to build your own controls or use a package like [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls) or [react-native-video-player](https://github.com/cornedor/react-native-video-player).
|
||||||
|
|
||||||
Platforms: iOS, react-native-dom
|
Platforms: Android ExoPlayer, iOS, react-native-dom
|
||||||
|
|
||||||
#### disableFocus
|
#### disableFocus
|
||||||
Determines whether video audio should override background music/audio in Android devices.
|
Determines whether video audio should override background music/audio in Android devices.
|
||||||
@ -482,6 +487,18 @@ maxBitRate={2000000} // 2 megabits
|
|||||||
|
|
||||||
Platforms: Android ExoPlayer, iOS
|
Platforms: Android ExoPlayer, iOS
|
||||||
|
|
||||||
|
#### minLoadRetryCount
|
||||||
|
Sets the minimum number of times to retry loading data before failing and reporting an error to the application. Useful to recover from transient internet failures.
|
||||||
|
|
||||||
|
Default: 3. Retry 3 times.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
minLoadRetryCount={5} // retry 5 times
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer
|
||||||
|
|
||||||
#### muted
|
#### muted
|
||||||
Controls whether the audio is muted
|
Controls whether the audio is muted
|
||||||
* **false (default)** - Don't mute audio
|
* **false (default)** - Don't mute audio
|
||||||
@ -496,6 +513,13 @@ Controls whether the media is paused
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### pictureInPicture
|
||||||
|
Determine whether the media should played as picture in picture.
|
||||||
|
* **false (default)** - Don't not play as picture in picture
|
||||||
|
* **true** - Play the media as picture in picture
|
||||||
|
|
||||||
|
Platforms: iOS
|
||||||
|
|
||||||
#### playInBackground
|
#### playInBackground
|
||||||
Determine whether the media should continue playing while the app is in the background. This allows customers to continue listening to the audio.
|
Determine whether the media should continue playing while the app is in the background. This allows customers to continue listening to the audio.
|
||||||
* **false (default)** - Don't continue playing the media
|
* **false (default)** - Don't continue playing the media
|
||||||
@ -936,6 +960,22 @@ Example:
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### onPictureInPictureStatusChanged
|
||||||
|
Callback function that is called when picture in picture becomes active or inactive.
|
||||||
|
|
||||||
|
Property | Type | Description
|
||||||
|
--- | --- | ---
|
||||||
|
isActive | boolean | Boolean indicating whether picture in picture is active
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms: iOS
|
||||||
|
|
||||||
#### onProgress
|
#### onProgress
|
||||||
Callback function that is called every progressUpdateInterval seconds with info about which position the media is currently playing.
|
Callback function that is called every progressUpdateInterval seconds with info about which position the media is currently playing.
|
||||||
|
|
||||||
@ -979,6 +1019,13 @@ Both the currentTime & seekTime are reported because the video player may not se
|
|||||||
|
|
||||||
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP
|
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP
|
||||||
|
|
||||||
|
#### onRestoreUserInterfaceForPictureInPictureStop
|
||||||
|
Callback function that corresponds to Apple's [`restoreUserInterfaceForPictureInPictureStopWithCompletionHandler`](https://developer.apple.com/documentation/avkit/avpictureinpicturecontrollerdelegate/1614703-pictureinpicturecontroller?language=objc). Call `restoreUserInterfaceForPictureInPictureStopCompleted` inside of this function when done restoring the user interface.
|
||||||
|
|
||||||
|
Payload: none
|
||||||
|
|
||||||
|
Platforms: iOS
|
||||||
|
|
||||||
#### onTimedMetadata
|
#### onTimedMetadata
|
||||||
Callback function that is called when timed metadata becomes available
|
Callback function that is called when timed metadata becomes available
|
||||||
|
|
||||||
@ -1067,6 +1114,18 @@ Future:
|
|||||||
|
|
||||||
Platforms: iOS
|
Platforms: iOS
|
||||||
|
|
||||||
|
#### restoreUserInterfaceForPictureInPictureStopCompleted
|
||||||
|
`restoreUserInterfaceForPictureInPictureStopCompleted(restored)`
|
||||||
|
|
||||||
|
This function corresponds to the completion handler in Apple's [restoreUserInterfaceForPictureInPictureStop](https://developer.apple.com/documentation/avkit/avpictureinpicturecontrollerdelegate/1614703-pictureinpicturecontroller?language=objc). IMPORTANT: This function must be called after `onRestoreUserInterfaceForPictureInPictureStop` is called.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
this.player.restoreUserInterfaceForPictureInPictureStopCompleted(true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms: iOS
|
||||||
|
|
||||||
#### seek()
|
#### seek()
|
||||||
`seek(seconds)`
|
`seek(seconds)`
|
||||||
|
|
||||||
|
43
Video.js
43
Video.js
@ -78,6 +78,10 @@ export default class Video extends Component {
|
|||||||
return await NativeModules.VideoManager.save(options, findNodeHandle(this._root));
|
return await NativeModules.VideoManager.save(options, findNodeHandle(this._root));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restoreUserInterfaceForPictureInPictureStopCompleted = (restored) => {
|
||||||
|
this.setNativeProps({ restoreUserInterfaceForPIPStopCompletionHandler: restored });
|
||||||
|
};
|
||||||
|
|
||||||
_assignRoot = (component) => {
|
_assignRoot = (component) => {
|
||||||
this._root = component;
|
this._root = component;
|
||||||
};
|
};
|
||||||
@ -198,6 +202,18 @@ export default class Video extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onPictureInPictureStatusChanged = (event) => {
|
||||||
|
if (this.props.onPictureInPictureStatusChanged) {
|
||||||
|
this.props.onPictureInPictureStatusChanged(event.nativeEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_onRestoreUserInterfaceForPictureInPictureStop = (event) => {
|
||||||
|
if (this.props.onRestoreUserInterfaceForPictureInPictureStop) {
|
||||||
|
this.props.onRestoreUserInterfaceForPictureInPictureStop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
_onAudioFocusChanged = (event) => {
|
_onAudioFocusChanged = (event) => {
|
||||||
if (this.props.onAudioFocusChanged) {
|
if (this.props.onAudioFocusChanged) {
|
||||||
this.props.onAudioFocusChanged(event.nativeEvent);
|
this.props.onAudioFocusChanged(event.nativeEvent);
|
||||||
@ -210,6 +226,13 @@ export default class Video extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getViewManagerConfig = viewManagerName => {
|
||||||
|
if (!NativeModules.UIManager.getViewManagerConfig) {
|
||||||
|
return NativeModules.UIManager[viewManagerName];
|
||||||
|
}
|
||||||
|
return NativeModules.UIManager.getViewManagerConfig(viewManagerName);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const resizeMode = this.props.resizeMode;
|
const resizeMode = this.props.resizeMode;
|
||||||
const source = resolveAssetSource(this.props.source) || {};
|
const source = resolveAssetSource(this.props.source) || {};
|
||||||
@ -220,18 +243,24 @@ export default class Video extends Component {
|
|||||||
uri = `file://${uri}`;
|
uri = `file://${uri}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!uri) {
|
||||||
|
console.warn('Trying to load empty source.');
|
||||||
|
}
|
||||||
|
|
||||||
const isNetwork = !!(uri && uri.match(/^https?:/));
|
const isNetwork = !!(uri && uri.match(/^https?:/));
|
||||||
const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/));
|
const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/));
|
||||||
|
|
||||||
let nativeResizeMode;
|
let nativeResizeMode;
|
||||||
|
const RCTVideoInstance = this.getViewManagerConfig('RCTVideo');
|
||||||
|
|
||||||
if (resizeMode === VideoResizeMode.stretch) {
|
if (resizeMode === VideoResizeMode.stretch) {
|
||||||
nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleToFill;
|
nativeResizeMode = RCTVideoInstance.Constants.ScaleToFill;
|
||||||
} else if (resizeMode === VideoResizeMode.contain) {
|
} else if (resizeMode === VideoResizeMode.contain) {
|
||||||
nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleAspectFit;
|
nativeResizeMode = RCTVideoInstance.Constants.ScaleAspectFit;
|
||||||
} else if (resizeMode === VideoResizeMode.cover) {
|
} else if (resizeMode === VideoResizeMode.cover) {
|
||||||
nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleAspectFill;
|
nativeResizeMode = RCTVideoInstance.Constants.ScaleAspectFill;
|
||||||
} else {
|
} else {
|
||||||
nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleNone;
|
nativeResizeMode = RCTVideoInstance.Constants.ScaleNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nativeProps = Object.assign({}, this.props);
|
const nativeProps = Object.assign({}, this.props);
|
||||||
@ -269,6 +298,8 @@ export default class Video extends Component {
|
|||||||
onPlaybackRateChange: this._onPlaybackRateChange,
|
onPlaybackRateChange: this._onPlaybackRateChange,
|
||||||
onAudioFocusChanged: this._onAudioFocusChanged,
|
onAudioFocusChanged: this._onAudioFocusChanged,
|
||||||
onAudioBecomingNoisy: this._onAudioBecomingNoisy,
|
onAudioBecomingNoisy: this._onAudioBecomingNoisy,
|
||||||
|
onPictureInPictureStatusChanged: this._onPictureInPictureStatusChanged,
|
||||||
|
onRestoreUserInterfaceForPictureInPictureStop: this._onRestoreUserInterfaceForPictureInPictureStop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const posterStyle = {
|
const posterStyle = {
|
||||||
@ -341,6 +372,7 @@ Video.propTypes = {
|
|||||||
// Opaque type returned by require('./video.mp4')
|
// Opaque type returned by require('./video.mp4')
|
||||||
PropTypes.number
|
PropTypes.number
|
||||||
]),
|
]),
|
||||||
|
minLoadRetryCount: PropTypes.number,
|
||||||
maxBitRate: PropTypes.number,
|
maxBitRate: PropTypes.number,
|
||||||
resizeMode: PropTypes.string,
|
resizeMode: PropTypes.string,
|
||||||
poster: PropTypes.string,
|
poster: PropTypes.string,
|
||||||
@ -391,6 +423,7 @@ Video.propTypes = {
|
|||||||
}),
|
}),
|
||||||
stereoPan: PropTypes.number,
|
stereoPan: PropTypes.number,
|
||||||
rate: PropTypes.number,
|
rate: PropTypes.number,
|
||||||
|
pictureInPicture: PropTypes.bool,
|
||||||
playInBackground: PropTypes.bool,
|
playInBackground: PropTypes.bool,
|
||||||
playWhenInactive: PropTypes.bool,
|
playWhenInactive: PropTypes.bool,
|
||||||
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
|
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
|
||||||
@ -422,6 +455,8 @@ Video.propTypes = {
|
|||||||
onPlaybackRateChange: PropTypes.func,
|
onPlaybackRateChange: PropTypes.func,
|
||||||
onAudioFocusChanged: PropTypes.func,
|
onAudioFocusChanged: PropTypes.func,
|
||||||
onAudioBecomingNoisy: PropTypes.func,
|
onAudioBecomingNoisy: PropTypes.func,
|
||||||
|
onPictureInPictureStatusChanged: PropTypes.func,
|
||||||
|
needsToRestoreUserInterfaceForPictureInPictureStop: PropTypes.func,
|
||||||
onExternalPlaybackChange: PropTypes.func,
|
onExternalPlaybackChange: PropTypes.func,
|
||||||
|
|
||||||
/* Required by react-native */
|
/* Required by react-native */
|
||||||
|
@ -8,6 +8,11 @@ android {
|
|||||||
compileSdkVersion safeExtGet('compileSdkVersion', 27)
|
compileSdkVersion safeExtGet('compileSdkVersion', 27)
|
||||||
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
|
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||||
targetSdkVersion safeExtGet('targetSdkVersion', 27)
|
targetSdkVersion safeExtGet('targetSdkVersion', 27)
|
||||||
|
@ -64,6 +64,7 @@ import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
|||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||||
|
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
@ -96,6 +97,9 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final VideoEventEmitter eventEmitter;
|
private final VideoEventEmitter eventEmitter;
|
||||||
|
private PlayerControlView playerControlView;
|
||||||
|
private View playPauseControlContainer;
|
||||||
|
private Player.EventListener eventListener;
|
||||||
|
|
||||||
private Handler mainHandler;
|
private Handler mainHandler;
|
||||||
private ExoPlayerView exoPlayerView;
|
private ExoPlayerView exoPlayerView;
|
||||||
@ -114,6 +118,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean isBuffering;
|
private boolean isBuffering;
|
||||||
private float rate = 1f;
|
private float rate = 1f;
|
||||||
private float audioVolume = 1f;
|
private float audioVolume = 1f;
|
||||||
|
private int minLoadRetryCount = 3;
|
||||||
private int maxBitRate = 0;
|
private int maxBitRate = 0;
|
||||||
private long seekTime = C.TIME_UNSET;
|
private long seekTime = C.TIME_UNSET;
|
||||||
|
|
||||||
@ -257,6 +262,76 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggling the visibility of the player control view
|
||||||
|
*/
|
||||||
|
private void togglePlayerControlVisibility() {
|
||||||
|
reLayout(playerControlView);
|
||||||
|
if (playerControlView.isVisible()) {
|
||||||
|
playerControlView.hide();
|
||||||
|
} else {
|
||||||
|
playerControlView.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializing Player control
|
||||||
|
*/
|
||||||
|
private void initializePlayerControl() {
|
||||||
|
if (playerControlView == null) {
|
||||||
|
playerControlView = new PlayerControlView(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting the player for the playerControlView
|
||||||
|
playerControlView.setPlayer(player);
|
||||||
|
playerControlView.show();
|
||||||
|
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
|
||||||
|
|
||||||
|
// Invoking onClick event for exoplayerView
|
||||||
|
exoPlayerView.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
togglePlayerControlVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invoking onPlayerStateChanged event for Player
|
||||||
|
eventListener = new Player.EventListener() {
|
||||||
|
@Override
|
||||||
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
reLayout(playPauseControlContainer);
|
||||||
|
//Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
|
||||||
|
player.removeListener(eventListener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
player.addListener(eventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adding Player control to the frame layout
|
||||||
|
*/
|
||||||
|
private void addPlayerControl() {
|
||||||
|
LayoutParams layoutParams = new LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT);
|
||||||
|
playerControlView.setLayoutParams(layoutParams);
|
||||||
|
addView(playerControlView, 1, layoutParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the layout
|
||||||
|
* @param view view needs to update layout
|
||||||
|
*
|
||||||
|
* This is a workaround for the open bug in react-native: https://github.com/facebook/react-native/issues/17968
|
||||||
|
*/
|
||||||
|
private void reLayout(View view) {
|
||||||
|
if (view == null) return;
|
||||||
|
view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
|
||||||
|
view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight());
|
||||||
|
}
|
||||||
|
|
||||||
private void initializePlayer() {
|
private void initializePlayer() {
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
||||||
@ -302,6 +377,9 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
eventEmitter.loadStart();
|
eventEmitter.loadStart();
|
||||||
loadVideoStarted = true;
|
loadVideoStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initializing the playerControlView
|
||||||
|
initializePlayerControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
||||||
@ -310,12 +388,17 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case C.TYPE_SS:
|
case C.TYPE_SS:
|
||||||
return new SsMediaSource(uri, buildDataSourceFactory(false),
|
return new SsMediaSource(uri, buildDataSourceFactory(false),
|
||||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
||||||
|
minLoadRetryCount, SsMediaSource.DEFAULT_LIVE_PRESENTATION_DELAY_MS,
|
||||||
|
mainHandler, null);
|
||||||
case C.TYPE_DASH:
|
case C.TYPE_DASH:
|
||||||
return new DashMediaSource(uri, buildDataSourceFactory(false),
|
return new DashMediaSource(uri, buildDataSourceFactory(false),
|
||||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
||||||
|
minLoadRetryCount, DashMediaSource.DEFAULT_LIVE_PRESENTATION_DELAY_MS,
|
||||||
|
mainHandler, null);
|
||||||
case C.TYPE_HLS:
|
case C.TYPE_HLS:
|
||||||
return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null);
|
return new HlsMediaSource(uri, mediaDataSourceFactory,
|
||||||
|
minLoadRetryCount, mainHandler, null);
|
||||||
case C.TYPE_OTHER:
|
case C.TYPE_OTHER:
|
||||||
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
|
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
|
||||||
mainHandler, null);
|
mainHandler, null);
|
||||||
@ -517,6 +600,10 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
onBuffering(false);
|
onBuffering(false);
|
||||||
startProgressHandler();
|
startProgressHandler();
|
||||||
videoLoaded();
|
videoLoaded();
|
||||||
|
//Setting the visibility for the playerControlView
|
||||||
|
if(playerControlView != null) {
|
||||||
|
playerControlView.show();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ExoPlayer.STATE_ENDED:
|
case ExoPlayer.STATE_ENDED:
|
||||||
text += "ended";
|
text += "ended";
|
||||||
@ -870,7 +957,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
TrackGroup group = groups.get(i);
|
TrackGroup group = groups.get(i);
|
||||||
for (int j = 0; j < group.length; j++) {
|
for (int j = 0; j < group.length; j++) {
|
||||||
Format format = group.getFormat(j);
|
Format format = group.getFormat(j);
|
||||||
if (format.height == value.asInt()) {
|
if (format.height == height) {
|
||||||
groupIndex = i;
|
groupIndex = i;
|
||||||
tracks[0] = j;
|
tracks[0] = j;
|
||||||
break;
|
break;
|
||||||
@ -998,6 +1085,11 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) {
|
||||||
|
minLoadRetryCount = newMinLoadRetryCount;
|
||||||
|
releasePlayer();
|
||||||
|
initializePlayer();
|
||||||
|
}
|
||||||
|
|
||||||
public void setPlayInBackground(boolean playInBackground) {
|
public void setPlayInBackground(boolean playInBackground) {
|
||||||
this.playInBackground = playInBackground;
|
this.playInBackground = playInBackground;
|
||||||
@ -1056,4 +1148,17 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
releasePlayer();
|
releasePlayer();
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handling controls prop
|
||||||
|
*
|
||||||
|
* @param controls Controls prop, if true enable controls, if false disable them
|
||||||
|
*/
|
||||||
|
public void setControls(boolean controls) {
|
||||||
|
if (controls && exoPlayerView != null) {
|
||||||
|
addPlayerControl();
|
||||||
|
} else if (getChildAt(1) instanceof PlayerControlView && exoPlayerView != null) {
|
||||||
|
removeViewAt(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
|
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
|
||||||
private static final String PROP_SEEK = "seek";
|
private static final String PROP_SEEK = "seek";
|
||||||
private static final String PROP_RATE = "rate";
|
private static final String PROP_RATE = "rate";
|
||||||
|
private static final String PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount";
|
||||||
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
|
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
|
||||||
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
||||||
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
||||||
@ -57,6 +58,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
|
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
|
||||||
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
|
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
|
||||||
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
|
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
|
||||||
|
private static final String PROP_CONTROLS = "controls";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -230,6 +232,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setMaxBitRateModifier(maxBitRate);
|
videoView.setMaxBitRateModifier(maxBitRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_MIN_LOAD_RETRY_COUNT)
|
||||||
|
public void minLoadRetryCount(final ReactExoplayerView videoView, final int minLoadRetryCount) {
|
||||||
|
videoView.setMinLoadRetryCountModifier(minLoadRetryCount);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
|
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
|
||||||
public void setPlayInBackground(final ReactExoplayerView videoView, final boolean playInBackground) {
|
public void setPlayInBackground(final ReactExoplayerView videoView, final boolean playInBackground) {
|
||||||
videoView.setPlayInBackground(playInBackground);
|
videoView.setPlayInBackground(playInBackground);
|
||||||
@ -255,6 +262,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setHideShutterView(hideShutterView);
|
videoView.setHideShutterView(hideShutterView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_CONTROLS, defaultBoolean = false)
|
||||||
|
public void setControls(final ReactExoplayerView videoView, final boolean controls) {
|
||||||
|
videoView.setControls(controls);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_BUFFER_CONFIG)
|
@ReactProp(name = PROP_BUFFER_CONFIG)
|
||||||
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
|
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
|
||||||
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:layoutDirection="ltr"
|
||||||
|
android:background="#CC000000"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_prev"
|
||||||
|
style="@style/ExoMediaButton.Previous"/>
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_rew"
|
||||||
|
style="@style/ExoMediaButton.Rewind"/>
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/exo_play_pause_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center">
|
||||||
|
<ImageButton android:id="@id/exo_play"
|
||||||
|
style="@style/ExoMediaButton.Play"/>
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_pause"
|
||||||
|
style="@style/ExoMediaButton.Pause"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_ffwd"
|
||||||
|
style="@style/ExoMediaButton.FastForward"/>
|
||||||
|
|
||||||
|
<ImageButton android:id="@id/exo_next"
|
||||||
|
style="@style/ExoMediaButton.Next"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView android:id="@id/exo_position"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:textColor="#FFBEBEBE"/>
|
||||||
|
|
||||||
|
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||||
|
android:id="@id/exo_progress"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="26dp"/>
|
||||||
|
|
||||||
|
<TextView android:id="@id/exo_duration"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingLeft="4dp"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:textColor="#FFBEBEBE"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
3
examples/embed-and-fullscreen/.babelrc
Normal file
3
examples/embed-and-fullscreen/.babelrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"presets": ["react-native"]
|
||||||
|
}
|
6
examples/embed-and-fullscreen/.buckconfig
Normal file
6
examples/embed-and-fullscreen/.buckconfig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
[android]
|
||||||
|
target = Google Inc.:Google APIs:23
|
||||||
|
|
||||||
|
[maven_repositories]
|
||||||
|
central = https://repo1.maven.org/maven2
|
58
examples/embed-and-fullscreen/.flowconfig
Normal file
58
examples/embed-and-fullscreen/.flowconfig
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
[ignore]
|
||||||
|
; We fork some components by platform
|
||||||
|
.*/*[.]android.js
|
||||||
|
|
||||||
|
# We fork some components by platform.
|
||||||
|
.*/*[.]android.js
|
||||||
|
|
||||||
|
# Ignore templates with `@flow` in header
|
||||||
|
.*/local-cli/generator.*
|
||||||
|
|
||||||
|
# Ignore malformed json
|
||||||
|
.*/node_modules/y18n/test/.*\.json
|
||||||
|
|
||||||
|
# Ignore the website subdir
|
||||||
|
<PROJECT_ROOT>/website/.*
|
||||||
|
|
||||||
|
# Ignore BUCK generated dirs
|
||||||
|
|
||||||
|
<PROJECT_ROOT>/\.buckd/
|
||||||
|
|
||||||
|
; Ignore unexpected extra "@providesModule"
|
||||||
|
.*/node_modules/.*/node_modules/fbjs/.*
|
||||||
|
|
||||||
|
; Ignore duplicate module providers
|
||||||
|
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||||
|
; "node_modules/react-native" but in the source repo it is in the root
|
||||||
|
.*/Libraries/react-native/React.js
|
||||||
|
.*/Libraries/react-native/ReactNative.js
|
||||||
|
|
||||||
|
[include]
|
||||||
|
|
||||||
|
[libs]
|
||||||
|
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||||
|
node_modules/react-native/flow
|
||||||
|
flow/
|
||||||
|
|
||||||
|
[options]
|
||||||
|
module.system=haste
|
||||||
|
|
||||||
|
experimental.strict_type_args=true
|
||||||
|
|
||||||
|
munge_underscores=true
|
||||||
|
|
||||||
|
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||||
|
|
||||||
|
suppress_type=$FlowIssue
|
||||||
|
suppress_type=$FlowFixMe
|
||||||
|
suppress_type=$FixMe
|
||||||
|
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||||
|
|
||||||
|
unsafe.enable_getters_and_setters=true
|
||||||
|
|
||||||
|
[version]
|
||||||
|
|
||||||
|
^0.36.0
|
1
examples/embed-and-fullscreen/.gitattributes
vendored
Normal file
1
examples/embed-and-fullscreen/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.pbxproj -text
|
53
examples/embed-and-fullscreen/.gitignore
vendored
Normal file
53
examples/embed-and-fullscreen/.gitignore
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# OSX
|
||||||
|
#
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata
|
||||||
|
*.xccheckout
|
||||||
|
*.moved-aside
|
||||||
|
DerivedData
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.xcuserstate
|
||||||
|
project.xcworkspace
|
||||||
|
|
||||||
|
# Android/IntelliJ
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# node.js
|
||||||
|
#
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
|
||||||
|
# BUCK
|
||||||
|
buck-out/
|
||||||
|
\.buckd/
|
||||||
|
android/app/libs
|
||||||
|
*.keystore
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||||
|
# screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots
|
144
examples/embed-and-fullscreen/.vscode/.react/debuggerWorker.js
vendored
Normal file
144
examples/embed-and-fullscreen/.vscode/.react/debuggerWorker.js
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
// Initialize some variables before react-native code would access them
|
||||||
|
var onmessage=null, self=global;
|
||||||
|
// Cache Node's original require as __debug__.require
|
||||||
|
global.__debug__={require: require};
|
||||||
|
// avoid Node's GLOBAL deprecation warning
|
||||||
|
Object.defineProperty(global, "GLOBAL", {
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
value: global
|
||||||
|
});
|
||||||
|
// Prevent leaking process.versions from debugger process to
|
||||||
|
// worker because pure React Native doesn't do that and some packages as js-md5 rely on this behavior
|
||||||
|
Object.defineProperty(process, "versions", {
|
||||||
|
value: undefined
|
||||||
|
});
|
||||||
|
var vscodeHandlers = {
|
||||||
|
'vscode_reloadApp': function () {
|
||||||
|
try {
|
||||||
|
global.require('NativeModules').DevMenu.reload();
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'vscode_showDevMenu': function () {
|
||||||
|
try {
|
||||||
|
var DevMenu = global.require('NativeModules').DevMenu.show();
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process.on("message", function (message) {
|
||||||
|
if (message.data && vscodeHandlers[message.data.method]) {
|
||||||
|
vscodeHandlers[message.data.method]();
|
||||||
|
} else if(onmessage) {
|
||||||
|
onmessage(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var postMessage = function(message){
|
||||||
|
process.send(message);
|
||||||
|
};
|
||||||
|
if (!self.postMessage) {
|
||||||
|
self.postMessage = postMessage;
|
||||||
|
}
|
||||||
|
var importScripts = (function(){
|
||||||
|
var fs=require('fs'), vm=require('vm');
|
||||||
|
return function(scriptUrl){
|
||||||
|
var scriptCode = fs.readFileSync(scriptUrl, "utf8");
|
||||||
|
vm.runInThisContext(scriptCode, {filename: scriptUrl});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global __fbBatchedBridge, self, importScripts, postMessage, onmessage: true */
|
||||||
|
/* eslint no-unused-vars: 0 */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
onmessage = (function() {
|
||||||
|
var visibilityState;
|
||||||
|
var showVisibilityWarning = (function() {
|
||||||
|
var hasWarned = false;
|
||||||
|
return function() {
|
||||||
|
// Wait until `YellowBox` gets initialized before displaying the warning.
|
||||||
|
if (hasWarned || console.warn.toString().includes('[native code]')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasWarned = true;
|
||||||
|
console.warn(
|
||||||
|
'Remote debugger is in a background tab which may cause apps to ' +
|
||||||
|
'perform slowly. Fix this by foregrounding the tab (or opening it in ' +
|
||||||
|
'a separate window).',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
var messageHandlers = {
|
||||||
|
executeApplicationScript: function(message, sendReply) {
|
||||||
|
for (var key in message.inject) {
|
||||||
|
self[key] = JSON.parse(message.inject[key]);
|
||||||
|
}
|
||||||
|
var error;
|
||||||
|
try {
|
||||||
|
importScripts(message.url);
|
||||||
|
} catch (err) {
|
||||||
|
error = err.message;
|
||||||
|
}
|
||||||
|
sendReply(null /* result */, error);
|
||||||
|
},
|
||||||
|
setDebuggerVisibility: function(message) {
|
||||||
|
visibilityState = message.visibilityState;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(message) {
|
||||||
|
if (visibilityState === 'hidden') {
|
||||||
|
showVisibilityWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
var object = message.data;
|
||||||
|
|
||||||
|
var sendReply = function(result, error) {
|
||||||
|
postMessage({replyID: object.id, result: result, error: error});
|
||||||
|
};
|
||||||
|
|
||||||
|
var handler = messageHandlers[object.method];
|
||||||
|
if (handler) {
|
||||||
|
// Special cased handlers
|
||||||
|
handler(object, sendReply);
|
||||||
|
} else {
|
||||||
|
// Other methods get called on the bridge
|
||||||
|
var returnValue = [[], [], [], 0];
|
||||||
|
var error;
|
||||||
|
try {
|
||||||
|
if (typeof __fbBatchedBridge === 'object') {
|
||||||
|
returnValue = __fbBatchedBridge[object.method].apply(
|
||||||
|
null,
|
||||||
|
object.arguments,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
error = 'Failed to call function, __fbBatchedBridge is undefined';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
error = err.message;
|
||||||
|
} finally {
|
||||||
|
sendReply(JSON.stringify(returnValue), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Notify debugger that we're done with loading
|
||||||
|
// and started listening for IPC messages
|
||||||
|
postMessage({workerLoaded:true});
|
80702
examples/embed-and-fullscreen/.vscode/.react/index.ios.bundle
vendored
Normal file
80702
examples/embed-and-fullscreen/.vscode/.react/index.ios.bundle
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
examples/embed-and-fullscreen/.vscode/.react/index.ios.map
vendored
Normal file
1
examples/embed-and-fullscreen/.vscode/.react/index.ios.map
vendored
Normal file
File diff suppressed because one or more lines are too long
79
examples/embed-and-fullscreen/.vscode/launch.json
vendored
Normal file
79
examples/embed-and-fullscreen/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Android Simulator",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "android",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"trace": "log",
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Android Device",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "android",
|
||||||
|
"target": "device",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug iPhone 6",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "ios",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"trace": "log",
|
||||||
|
"target": "iPhone 6",
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug iPhone X",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "ios",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"trace": "log",
|
||||||
|
"target": "iPhone X",
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug iPad Air",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "ios",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"trace": "log",
|
||||||
|
"target": "iPad Air",
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug iOS Device",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "launch",
|
||||||
|
"platform": "ios",
|
||||||
|
"target": "device",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach to packager",
|
||||||
|
"program": "${workspaceRoot}/.vscode/launchReactNative.js",
|
||||||
|
"type": "reactnative",
|
||||||
|
"request": "attach",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outDir": "${workspaceRoot}/.vscode/.react"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
examples/embed-and-fullscreen/.vscode/settings.json
vendored
Normal file
5
examples/embed-and-fullscreen/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"java.configuration.updateBuildConfiguration": "disabled"
|
||||||
|
}
|
1
examples/embed-and-fullscreen/.watchmanconfig
Normal file
1
examples/embed-and-fullscreen/.watchmanconfig
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
31
examples/embed-and-fullscreen/Utils.js
Normal file
31
examples/embed-and-fullscreen/Utils.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Dimensions } from "react-native";
|
||||||
|
|
||||||
|
export default function Util() {}
|
||||||
|
|
||||||
|
Util.isPortrait = () => {
|
||||||
|
const dim = Dimensions.get("screen");
|
||||||
|
return dim.height >= dim.width;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a timer for video from< time in seconds
|
||||||
|
* ~~ is used as faster substitute for Math.floor() function
|
||||||
|
* https://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript
|
||||||
|
* @param time
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
Util.secondToTime = (time) => {
|
||||||
|
return ~~(time / 60) + ":" + (time % 60 < 10 ? "0" : "") + time % 60;
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.normalizeSeconds = (number) => {
|
||||||
|
let sec_num = parseInt(number, 10); // don't forget the second param
|
||||||
|
let hours = Math.floor(sec_num / 3600);
|
||||||
|
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||||
|
let seconds = sec_num - (hours * 3600) - (minutes * 60);
|
||||||
|
|
||||||
|
if (hours < 10) {hours = "0"+hours;}
|
||||||
|
if (minutes < 10) {minutes = "0"+minutes;}
|
||||||
|
if (seconds < 10) {seconds = "0"+seconds;}
|
||||||
|
return hours+':'+minutes+':'+seconds;
|
||||||
|
};
|
BIN
examples/embed-and-fullscreen/broadchurch.mp4
Normal file
BIN
examples/embed-and-fullscreen/broadchurch.mp4
Normal file
Binary file not shown.
81
examples/embed-and-fullscreen/index.ios.js
Normal file
81
examples/embed-and-fullscreen/index.ios.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
import React, {
|
||||||
|
Component
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AppRegistry,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
Dimensions,
|
||||||
|
Text,
|
||||||
|
Button
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import Util from './Utils'
|
||||||
|
|
||||||
|
import Video from 'react-native-video';
|
||||||
|
|
||||||
|
export default class VideoPlayer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onLayout = this.onLayout.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.resizeVideoPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <View
|
||||||
|
onLayout={this.onLayout}
|
||||||
|
style={styles.container}>
|
||||||
|
<Text>Here's some pre-Text</Text>
|
||||||
|
<Video
|
||||||
|
ref={p => { this.videoPlayer = p; }}
|
||||||
|
source={require('./broadchurch.mp4')}
|
||||||
|
style={{width: this.state.orientationWidth, height: this.state.orientationHeight }}
|
||||||
|
controls={true}
|
||||||
|
/>
|
||||||
|
<Button title="full screen" onPress={ this.onPress.bind(this) }></Button>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
|
||||||
|
onPress() {
|
||||||
|
if (this.videoPlayer!=null)
|
||||||
|
this.videoPlayer.presentFullscreenPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeVideoPlayer() {
|
||||||
|
// Always in 16 /9 aspect ratio
|
||||||
|
let {width, height} = Dimensions.get('window');
|
||||||
|
|
||||||
|
if (Util.isPortrait()) {
|
||||||
|
this.setState({
|
||||||
|
orientationWidth: width * 0.8,
|
||||||
|
orientationHeight: width * 0.8 * 0.56,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
orientationHeight: height * 0.8,
|
||||||
|
orientationWidth: height * 0.8 * 1.77
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLayout(e) {
|
||||||
|
console.log('on layout called');
|
||||||
|
this.resizeVideoPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer);
|
210
examples/embed-and-fullscreen/index.windows.js
Normal file
210
examples/embed-and-fullscreen/index.windows.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
Component
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AppRegistry,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import Video from 'react-native-video';
|
||||||
|
|
||||||
|
class VideoPlayer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onLoad = this.onLoad.bind(this);
|
||||||
|
this.onProgress = this.onProgress.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
rate: 1,
|
||||||
|
volume: 1,
|
||||||
|
muted: false,
|
||||||
|
resizeMode: 'contain',
|
||||||
|
duration: 0.0,
|
||||||
|
currentTime: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad(data) {
|
||||||
|
this.setState({duration: data.duration});
|
||||||
|
}
|
||||||
|
|
||||||
|
onProgress(data) {
|
||||||
|
this.setState({currentTime: data.currentTime});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentTimePercentage() {
|
||||||
|
if (this.state.currentTime > 0) {
|
||||||
|
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRateControl(rate) {
|
||||||
|
const isSelected = (this.state.rate == rate);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={() => { this.setState({rate: rate}) }}>
|
||||||
|
<Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
|
||||||
|
{rate}x
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderResizeModeControl(resizeMode) {
|
||||||
|
const isSelected = (this.state.resizeMode == resizeMode);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={() => { this.setState({resizeMode: resizeMode}) }}>
|
||||||
|
<Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
|
||||||
|
{resizeMode}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVolumeControl(volume) {
|
||||||
|
const isSelected = (this.state.volume == volume);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={() => { this.setState({volume: volume}) }}>
|
||||||
|
<Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
|
||||||
|
{volume * 100}%
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const flexCompleted = this.getCurrentTimePercentage() * 100;
|
||||||
|
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<TouchableOpacity style={styles.fullScreen} onPress={() => {this.setState({paused: !this.state.paused})}}>
|
||||||
|
<Video source={require('./broadchurch.mp4')}
|
||||||
|
style={styles.fullScreen}
|
||||||
|
rate={this.state.rate}
|
||||||
|
paused={this.state.paused}
|
||||||
|
volume={this.state.volume}
|
||||||
|
muted={this.state.muted}
|
||||||
|
resizeMode={this.state.resizeMode}
|
||||||
|
onLoad={this.onLoad}
|
||||||
|
onProgress={this.onProgress}
|
||||||
|
onEnd={() => { console.log('Done!') }}
|
||||||
|
repeat={true} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View style={styles.controls}>
|
||||||
|
<View style={styles.generalControls}>
|
||||||
|
<View style={styles.rateControl}>
|
||||||
|
{this.renderRateControl(0.25)}
|
||||||
|
{this.renderRateControl(0.5)}
|
||||||
|
{this.renderRateControl(1.0)}
|
||||||
|
{this.renderRateControl(1.5)}
|
||||||
|
{this.renderRateControl(2.0)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.volumeControl}>
|
||||||
|
{this.renderVolumeControl(0.5)}
|
||||||
|
{this.renderVolumeControl(1)}
|
||||||
|
{this.renderVolumeControl(1.5)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.resizeModeControl}>
|
||||||
|
{this.renderResizeModeControl('cover')}
|
||||||
|
{this.renderResizeModeControl('contain')}
|
||||||
|
{this.renderResizeModeControl('stretch')}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.trackingControls}>
|
||||||
|
<View style={styles.progress}>
|
||||||
|
<View style={[styles.innerProgressCompleted, {flex: flexCompleted}]} />
|
||||||
|
<View style={[styles.innerProgressRemaining, {flex: flexRemaining}]} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
},
|
||||||
|
fullScreen: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
controls: {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
borderRadius: 5,
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 20,
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderRadius: 3,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
innerProgressCompleted: {
|
||||||
|
height: 20,
|
||||||
|
backgroundColor: '#cccccc',
|
||||||
|
},
|
||||||
|
innerProgressRemaining: {
|
||||||
|
height: 20,
|
||||||
|
backgroundColor: '#2C2C2C',
|
||||||
|
},
|
||||||
|
generalControls: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderRadius: 4,
|
||||||
|
overflow: 'hidden',
|
||||||
|
paddingBottom: 10,
|
||||||
|
},
|
||||||
|
rateControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
volumeControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
resizeModeControl: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
controlOption: {
|
||||||
|
alignSelf: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
color: "white",
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingRight: 2,
|
||||||
|
lineHeight: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer);
|
16
examples/embed-and-fullscreen/ios/AppDelegate.h
Normal file
16
examples/embed-and-fullscreen/ios/AppDelegate.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
|
@property (nonatomic, strong) UIWindow *window;
|
||||||
|
|
||||||
|
@end
|
52
examples/embed-and-fullscreen/ios/AppDelegate.m
Normal file
52
examples/embed-and-fullscreen/ios/AppDelegate.m
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import "RCTRootView.h"
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
NSURL *jsCodeLocation;
|
||||||
|
|
||||||
|
// Loading JavaScript code - uncomment the one you want.
|
||||||
|
|
||||||
|
// OPTION 1
|
||||||
|
// Load from development server. Start the server from the repository root:
|
||||||
|
//
|
||||||
|
// $ npm start
|
||||||
|
//
|
||||||
|
// To run on device, change `localhost` to the IP address of your computer, and make sure your computer and
|
||||||
|
// iOS device are on the same Wi-Fi network.
|
||||||
|
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
|
// OPTION 2
|
||||||
|
// Load from pre-bundled file on disk. To re-generate the static bundle, run
|
||||||
|
//
|
||||||
|
// $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
|
||||||
|
//
|
||||||
|
// and uncomment the next following line
|
||||||
|
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
|
|
||||||
|
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||||
|
moduleName:@"VideoPlayer"
|
||||||
|
initialProperties: nil
|
||||||
|
launchOptions:launchOptions];
|
||||||
|
|
||||||
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||||
|
UIViewController *rootViewController = [[UIViewController alloc] init];
|
||||||
|
rootViewController.view = rootView;
|
||||||
|
self.window.rootViewController = rootViewController;
|
||||||
|
[self.window makeKeyAndVisible];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
|
||||||
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||||
|
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VideoPlayer" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||||
|
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||||
|
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||||
|
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||||
|
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||||
|
</constraints>
|
||||||
|
<nil key="simulatedStatusBarMetrics"/>
|
||||||
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
|
<point key="canvasLocation" x="548" y="455"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
47
examples/embed-and-fullscreen/ios/Info.plist
Normal file
47
examples/embed-and-fullscreen/ios/Info.plist
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>2</string>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string></string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
53
examples/embed-and-fullscreen/ios/VideoPlayer-tvOS.plist
Normal file
53
examples/embed-and-fullscreen/ios/VideoPlayer-tvOS.plist
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string></string>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1000"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "NO"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "2D2A28121D9B038B00D4039D"
|
||||||
|
BuildableName = "libReact.a"
|
||||||
|
BlueprintName = "React-tvOS"
|
||||||
|
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "FA8681B6216D5C6D0010C92A"
|
||||||
|
BuildableName = "VideoPlayer-tvOS.app"
|
||||||
|
BlueprintName = "VideoPlayer-tvOS"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "FA8681B6216D5C6D0010C92A"
|
||||||
|
BuildableName = "VideoPlayer-tvOS.app"
|
||||||
|
BlueprintName = "VideoPlayer-tvOS"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "FA8681B6216D5C6D0010C92A"
|
||||||
|
BuildableName = "VideoPlayer-tvOS.app"
|
||||||
|
BlueprintName = "VideoPlayer-tvOS"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "FA8681B6216D5C6D0010C92A"
|
||||||
|
BuildableName = "VideoPlayer-tvOS.app"
|
||||||
|
BlueprintName = "VideoPlayer-tvOS"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "0620"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "NO"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
|
||||||
|
BuildableName = "libReact.a"
|
||||||
|
BlueprintName = "React"
|
||||||
|
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "VideoPlayer.app"
|
||||||
|
BlueprintName = "VideoPlayer"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "VideoPlayer.app"
|
||||||
|
BlueprintName = "VideoPlayer"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "VideoPlayer.app"
|
||||||
|
BlueprintName = "VideoPlayer"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "VideoPlayer.app"
|
||||||
|
BlueprintName = "VideoPlayer"
|
||||||
|
ReferencedContainer = "container:VideoPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
16
examples/embed-and-fullscreen/ios/VideoPlayer/AppDelegate.h
Normal file
16
examples/embed-and-fullscreen/ios/VideoPlayer/AppDelegate.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
|
@property (nonatomic, strong) UIWindow *window;
|
||||||
|
|
||||||
|
@end
|
37
examples/embed-and-fullscreen/ios/VideoPlayer/AppDelegate.m
Normal file
37
examples/embed-and-fullscreen/ios/VideoPlayer/AppDelegate.m
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <React/RCTRootView.h>
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
NSURL *jsCodeLocation;
|
||||||
|
|
||||||
|
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
|
||||||
|
|
||||||
|
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||||
|
moduleName:@"VideoPlayer"
|
||||||
|
initialProperties:nil
|
||||||
|
launchOptions:launchOptions];
|
||||||
|
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
|
||||||
|
|
||||||
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||||
|
UIViewController *rootViewController = [UIViewController new];
|
||||||
|
rootViewController.view = rootView;
|
||||||
|
self.window.rootViewController = rootViewController;
|
||||||
|
[self.window makeKeyAndVisible];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||||
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||||
|
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VideoPlayer" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||||
|
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||||
|
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||||
|
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||||
|
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||||
|
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||||
|
</constraints>
|
||||||
|
<nil key="simulatedStatusBarMetrics"/>
|
||||||
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
|
<point key="canvasLocation" x="548" y="455"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
54
examples/embed-and-fullscreen/ios/VideoPlayer/Info.plist
Normal file
54
examples/embed-and-fullscreen/ios/VideoPlayer/Info.plist
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string></string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
18
examples/embed-and-fullscreen/ios/VideoPlayer/main.m
Normal file
18
examples/embed-and-fullscreen/ios/VideoPlayer/main.m
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
|
}
|
||||||
|
}
|
5
examples/embed-and-fullscreen/ios/main.jsbundle
Normal file
5
examples/embed-and-fullscreen/ios/main.jsbundle
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Offline JS
|
||||||
|
// To re-generate the offline bundle, run this from root of your project
|
||||||
|
// $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
|
||||||
|
|
||||||
|
throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions');
|
19
examples/embed-and-fullscreen/ios/main.m
Normal file
19
examples/embed-and-fullscreen/ios/main.m
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
examples/embed-and-fullscreen/rn-cli.config.js
Normal file
7
examples/embed-and-fullscreen/rn-cli.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const blacklist = require('metro').createBlacklist;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getBlacklistRE: function() {
|
||||||
|
return blacklist([/node_modules\/react-native-video\/examples\/.*/]);
|
||||||
|
}
|
||||||
|
};
|
@ -15,8 +15,10 @@
|
|||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate>
|
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate>
|
||||||
#else
|
#elif TARGET_OS_TV
|
||||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
||||||
|
#else
|
||||||
|
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
||||||
@ -38,6 +40,8 @@
|
|||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume;
|
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange;
|
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoExternalPlaybackChange;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoExternalPlaybackChange;
|
||||||
|
@property (nonatomic, copy) RCTBubblingEventBlock onPictureInPictureStatusChanged;
|
||||||
|
@property (nonatomic, copy) RCTBubblingEventBlock onRestoreUserInterfaceForPictureInPictureStop;
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
BOOL _playbackStalled;
|
BOOL _playbackStalled;
|
||||||
BOOL _playInBackground;
|
BOOL _playInBackground;
|
||||||
BOOL _playWhenInactive;
|
BOOL _playWhenInactive;
|
||||||
|
BOOL _pictureInPicture;
|
||||||
NSString * _ignoreSilentSwitch;
|
NSString * _ignoreSilentSwitch;
|
||||||
NSString * _resizeMode;
|
NSString * _resizeMode;
|
||||||
BOOL _fullscreen;
|
BOOL _fullscreen;
|
||||||
@ -76,6 +77,10 @@ static int const RCTVideoUnset = -1;
|
|||||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||||
RCTVideoCache * _videoCache;
|
RCTVideoCache * _videoCache;
|
||||||
#endif
|
#endif
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)(BOOL);
|
||||||
|
AVPictureInPictureController *_pipController;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||||
@ -100,7 +105,11 @@ static int const RCTVideoUnset = -1;
|
|||||||
_playInBackground = false;
|
_playInBackground = false;
|
||||||
_allowsExternalPlayback = YES;
|
_allowsExternalPlayback = YES;
|
||||||
_playWhenInactive = false;
|
_playWhenInactive = false;
|
||||||
|
_pictureInPicture = false;
|
||||||
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
_restoreUserInterfaceForPIPStopCompletionHandler = NULL;
|
||||||
|
#endif
|
||||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||||
_videoCache = [RCTVideoCache sharedInstance];
|
_videoCache = [RCTVideoCache sharedInstance];
|
||||||
#endif
|
#endif
|
||||||
@ -137,7 +146,6 @@ static int const RCTVideoUnset = -1;
|
|||||||
|
|
||||||
viewController.view.frame = self.bounds;
|
viewController.view.frame = self.bounds;
|
||||||
viewController.player = player;
|
viewController.player = player;
|
||||||
viewController.view.frame = self.bounds;
|
|
||||||
return viewController;
|
return viewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +205,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self removePlayerLayer];
|
[self removePlayerLayer];
|
||||||
[self removePlayerItemObservers];
|
[self removePlayerItemObservers];
|
||||||
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
||||||
|
[_player removeObserver:self forKeyPath:externalPlaybackActive context: nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - App lifecycle handlers
|
#pragma mark - App lifecycle handlers
|
||||||
@ -466,6 +475,10 @@ static int const RCTVideoUnset = -1;
|
|||||||
bool shouldCache = [RCTConvert BOOL:[source objectForKey:@"shouldCache"]];
|
bool shouldCache = [RCTConvert BOOL:[source objectForKey:@"shouldCache"]];
|
||||||
NSString *uri = [source objectForKey:@"uri"];
|
NSString *uri = [source objectForKey:@"uri"];
|
||||||
NSString *type = [source objectForKey:@"type"];
|
NSString *type = [source objectForKey:@"type"];
|
||||||
|
if (!uri || [uri isEqualToString:@""]) {
|
||||||
|
DebugLog(@"Could not find video URL in source '%@'", source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NSURL *url = isNetwork || isAsset
|
NSURL *url = isNetwork || isAsset
|
||||||
? [NSURL URLWithString:uri]
|
? [NSURL URLWithString:uri]
|
||||||
@ -565,6 +578,27 @@ static int const RCTVideoUnset = -1;
|
|||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||||
{
|
{
|
||||||
|
// when controls==true, this is a hack to reset the rootview when rotation happens in fullscreen
|
||||||
|
if (object == _playerViewController.contentOverlayView) {
|
||||||
|
if ([keyPath isEqualToString:@"frame"]) {
|
||||||
|
|
||||||
|
CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue];
|
||||||
|
CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue];
|
||||||
|
|
||||||
|
if (!CGRectEqualToRect(oldRect, newRect)) {
|
||||||
|
if (CGRectEqualToRect(newRect, [UIScreen mainScreen].bounds)) {
|
||||||
|
NSLog(@"in fullscreen");
|
||||||
|
} else NSLog(@"not fullscreen");
|
||||||
|
|
||||||
|
[self.reactViewController.view setFrame:[UIScreen mainScreen].bounds];
|
||||||
|
[self.reactViewController.view setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else
|
||||||
|
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||||
|
}
|
||||||
|
|
||||||
if (object == _playerItem) {
|
if (object == _playerItem) {
|
||||||
// When timeMetadata is read the event onTimedMetadata is triggered
|
// When timeMetadata is read the event onTimedMetadata is triggered
|
||||||
if ([keyPath isEqualToString:timedMetadata]) {
|
if ([keyPath isEqualToString:timedMetadata]) {
|
||||||
@ -781,6 +815,44 @@ static int const RCTVideoUnset = -1;
|
|||||||
_playWhenInactive = playWhenInactive;
|
_playWhenInactive = playWhenInactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setPictureInPicture:(BOOL)pictureInPicture
|
||||||
|
{
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
if (_pictureInPicture == pictureInPicture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pictureInPicture = pictureInPicture;
|
||||||
|
if (_pipController && _pictureInPicture && ![_pipController isPictureInPictureActive]) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[_pipController startPictureInPicture];
|
||||||
|
});
|
||||||
|
} else if (_pipController && !_pictureInPicture && [_pipController isPictureInPictureActive]) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[_pipController stopPictureInPicture];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore
|
||||||
|
{
|
||||||
|
if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) {
|
||||||
|
_restoreUserInterfaceForPIPStopCompletionHandler(restore);
|
||||||
|
_restoreUserInterfaceForPIPStopCompletionHandler = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupPipController {
|
||||||
|
if (!_pipController && _playerLayer && [AVPictureInPictureController isPictureInPictureSupported]) {
|
||||||
|
// Create new controller passing reference to the AVPlayerLayer
|
||||||
|
_pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:_playerLayer];
|
||||||
|
_pipController.delegate = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch
|
- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch
|
||||||
{
|
{
|
||||||
_ignoreSilentSwitch = ignoreSilentSwitch;
|
_ignoreSilentSwitch = ignoreSilentSwitch;
|
||||||
@ -1215,8 +1287,15 @@ static int const RCTVideoUnset = -1;
|
|||||||
// to prevent video from being animated when resizeMode is 'cover'
|
// to prevent video from being animated when resizeMode is 'cover'
|
||||||
// resize mode must be set before subview is added
|
// resize mode must be set before subview is added
|
||||||
[self setResizeMode:_resizeMode];
|
[self setResizeMode:_resizeMode];
|
||||||
|
|
||||||
|
if (_controls) {
|
||||||
|
UIViewController *viewController = [self reactViewController];
|
||||||
|
[viewController addChildViewController:_playerViewController];
|
||||||
[self addSubview:_playerViewController.view];
|
[self addSubview:_playerViewController.view];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)usePlayerLayer
|
- (void)usePlayerLayer
|
||||||
@ -1235,6 +1314,9 @@ static int const RCTVideoUnset = -1;
|
|||||||
|
|
||||||
[self.layer addSublayer:_playerLayer];
|
[self.layer addSublayer:_playerLayer];
|
||||||
self.layer.needsDisplayOnBoundsChange = YES;
|
self.layer.needsDisplayOnBoundsChange = YES;
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
[self setupPipController];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1405,6 +1487,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
|
|
||||||
[self removePlayerLayer];
|
[self removePlayerLayer];
|
||||||
|
|
||||||
|
[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
|
||||||
[_playerViewController.view removeFromSuperview];
|
[_playerViewController.view removeFromSuperview];
|
||||||
_playerViewController = nil;
|
_playerViewController = nil;
|
||||||
|
|
||||||
@ -1491,4 +1574,44 @@ static int const RCTVideoUnset = -1;
|
|||||||
return array[0];
|
return array[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Picture in Picture
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||||
|
if (self.onPictureInPictureStatusChanged) {
|
||||||
|
self.onPictureInPictureStatusChanged(@{
|
||||||
|
@"isActive": [NSNumber numberWithBool:false]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||||
|
if (self.onPictureInPictureStatusChanged) {
|
||||||
|
self.onPictureInPictureStatusChanged(@{
|
||||||
|
@"isActive": [NSNumber numberWithBool:true]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL))completionHandler {
|
||||||
|
NSAssert(_restoreUserInterfaceForPIPStopCompletionHandler == NULL, @"restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited.");
|
||||||
|
if (self.onRestoreUserInterfaceForPictureInPictureStop) {
|
||||||
|
self.onRestoreUserInterfaceForPictureInPictureStop(@{});
|
||||||
|
}
|
||||||
|
_restoreUserInterfaceForPIPStopCompletionHandler = completionHandler;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -32,6 +32,7 @@ RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(volume, float);
|
RCT_EXPORT_VIEW_PROPERTY(volume, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(playWhenInactive, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(playWhenInactive, BOOL);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(pictureInPicture, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(rate, float);
|
RCT_EXPORT_VIEW_PROPERTY(rate, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary);
|
RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary);
|
||||||
@ -42,6 +43,7 @@ RCT_EXPORT_VIEW_PROPERTY(fullscreenOrientation, NSString);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(filter, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(filter, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
||||||
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
|
||||||
@ -77,6 +79,8 @@ RCT_REMAP_METHOD(save,
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTBubblingEventBlock);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTBubblingEventBlock);
|
||||||
|
|
||||||
- (NSDictionary *)constantsToExport
|
- (NSDictionary *)constantsToExport
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user