Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[expo-av] Fix fullscreen events not emitted on iOS #9323

Merged
merged 3 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/native-component-list/src/screens/AV/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export default class VideoPlayer extends React.Component<

_handleVideoMount = (ref: Video) => (this._video = ref);

_updateStateToStatus = (status: any) => this.setState(status);
_handlePlaybackStatusUpdate = (status: any) => this.setState(status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_handlePlaybackStatusUpdate = (status: any) => this.setState(status);
_handlePlaybackStatusUpdate = (status: SomeMeaningfulType) => this.setState(status);

Maybe it would help understanding what state parameters are actually changing? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree. I'll have at improving the example in a separate PR 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NLC update PR #9335


_handleFullScreenUpdate = (event: any) => console.log('onFullscreenUpdate', event);

_playAsync = async () => this._video!.playAsync();

Expand Down Expand Up @@ -83,7 +85,8 @@ export default class VideoPlayer extends React.Component<
onError={this._handleError}
style={{ height: 300 }}
progressUpdateIntervalMillis={100}
onPlaybackStatusUpdate={this._updateStateToStatus}
onPlaybackStatusUpdate={this._handlePlaybackStatusUpdate}
onFullscreenUpdate={this._handleFullScreenUpdate}
/>
);

Expand Down
1 change: 1 addition & 0 deletions packages/expo-av/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### 🐛 Bug fixes

- Fix audio recording after reload. ([#9283](https://github.com/expo/expo/pull/9283) by [@IjzerenHein](https://github.com/IjzerenHein))
- Fix fullscreen events not emitted on iOS. ([#9323](https://github.com/expo/expo/pull/9323) by [@IjzerenHein](https://github.com/IjzerenHein))

## 8.3.0 — 2020-07-08

Expand Down
2 changes: 1 addition & 1 deletion packages/expo-av/ios/EXAV/Video/EXVideoView.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#import <EXAV/EXAVObject.h>
#import <EXAV/EXVideoPlayerViewControllerDelegate.h>

@interface EXVideoView : UIView <EXVideoPlayerViewControllerDelegate, EXAVObject>
@interface EXVideoView : UIView <EXVideoPlayerViewControllerDelegate, AVPlayerViewControllerDelegate, EXAVObject>

typedef NS_OPTIONS(NSUInteger, EXVideoFullscreenUpdate)
{
Expand Down
48 changes: 40 additions & 8 deletions packages/expo-av/ios/EXAV/Video/EXVideoView.m
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ - (EXVideoPlayerViewController *)_createNewPlayerViewController
EXVideoPlayerViewController *controller = [[EXVideoPlayerViewController alloc] init];
[controller setShowsPlaybackControls:_useNativeControls];
[controller setRctDelegate:self];
[controller setDelegate:self];
[controller.view setFrame:self.bounds];
[controller setPlayer:_data.player];
[controller addObserver:self forKeyPath:EXVideoReadyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil];
Expand Down Expand Up @@ -199,7 +200,11 @@ - (void)_removePlayerViewController
if (strongSelf && strongSelf.playerViewController) {
[strongSelf.playerViewController.view removeFromSuperview];
[strongSelf.playerViewController removeObserver:strongSelf forKeyPath:EXVideoReadyForDisplayKeyPath];
[strongSelf.playerViewController removeObserver:strongSelf forKeyPath:EXVideoBoundsKeyPath];
if (@available(iOS 12, *)) {
// EXVideoBounds monitoring is only used as a fallback on iOS 11 or lower
} else {
[strongSelf.playerViewController removeObserver:strongSelf forKeyPath:EXVideoBoundsKeyPath];
}
strongSelf.playerViewController = nil;
}
};
Expand Down Expand Up @@ -238,11 +243,12 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
@"status": [_data getStatus]});
}
}
} else if (object == _playerViewController && [keyPath isEqualToString:EXVideoBoundsKeyPath]) {

// On iOS 11 or lower, use video-bounds monitoring to detect changes in the full-screen
// mode due to activating native controls
} else if (object == _playerViewController && [keyPath isEqualToString:EXVideoBoundsKeyPath]) {
CGRect viewBounds = [change[@"new"] CGRectValue];
CGRect screen = [[UIScreen mainScreen] bounds];

if (viewBounds.size.height != screen.size.height && viewBounds.size.width != screen.size.width && _fullscreenPlayerPresented && !_fullscreenPlayerViewController) {
// Fullscreen player is being dismissed
_fullscreenPlayerPresented = NO;
Expand Down Expand Up @@ -499,11 +505,15 @@ - (void)setUseNativeControls:(BOOL)useNativeControls
}
if (!strongSelf.playerViewController && strongSelf.data) {
strongSelf.playerViewController = [strongSelf _createNewPlayerViewController];
// We're listening for changes to `videoBounds`, because it seems
// to be the easiest way to detect fullscreen changes triggered by the native video button.
// See https://stackoverflow.com/questions/36323259/detect-video-playing-full-screen-in-portrait-or-landscape/36388184#36388184
// and https://github.com/expo/expo/issues/1566
[strongSelf.playerViewController addObserver:self forKeyPath:EXVideoBoundsKeyPath options:NSKeyValueObservingOptionNew context:nil];
if (@available(iOS 12, *)) {
// On iOS 12 or higher, use the AVPlayerViewControllerDelegate full-screen delegate methods:
// https://stackoverflow.com/a/58809976/3785358
} else {
// On iOS 11 or earlier, fallback to listening for changes to `videoBounds`.
// See https://stackoverflow.com/questions/36323259/detect-video-playing-full-screen-in-portrait-or-landscape/36388184#36388184
// and https://github.com/expo/expo/issues/1566
[strongSelf.playerViewController addObserver:self forKeyPath:EXVideoBoundsKeyPath options:NSKeyValueObservingOptionNew context:nil];
}
// Resize mode must be set before layer is added
// to prevent video from being animated when `resizeMode` is `cover`
[strongSelf _updateNativeResizeMode];
Expand Down Expand Up @@ -636,6 +646,28 @@ - (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerView
}
}

#pragma mark - AVVideoPlayerViewControllerDelegate

- (void)playerViewController:(AVPlayerViewController *)playerViewController
willBeginFullScreenPresentationWithAnimationCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
if (playerViewController == _playerViewController) {
_fullscreenPlayerPresented = YES;
[self _callFullscreenCallbackForUpdate:EXVideoFullscreenUpdatePlayerWillPresent];
[self _callFullscreenCallbackForUpdate:EXVideoFullscreenUpdatePlayerDidPresent];
}
}

- (void)playerViewController:(AVPlayerViewController *)playerViewController
willEndFullScreenPresentationWithAnimationCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
if (playerViewController == _playerViewController) {
_fullscreenPlayerPresented = NO;
[self _callFullscreenCallbackForUpdate:EXVideoFullscreenUpdatePlayerWillDismiss];
[self _callFullscreenCallbackForUpdate:EXVideoFullscreenUpdatePlayerDidDismiss];
}
}

#pragma mark - EXAVObject

- (void)pauseImmediately
Expand Down