Video fullscreen switch (#132)

* file page download progress display and media loading / playback tweaks
* Moved MediaPlayer component to be a child of the top-level view
* hide tracking controls when player controls are hidden in fullscreen mode
This commit is contained in:
akinwale 2018-05-19 16:11:59 +01:00 committed by GitHub
parent f850be1eca
commit ceeef6324e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 70 deletions

View file

@ -20,23 +20,30 @@ class MediaPlayer extends React.PureComponent {
seekerWidth = 0;
trackingOffset = 0;
tracking = null;
video = null;
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'stretch',
duration: 0.0,
currentTime: 0.0,
paused: true,
fullscreenMode: false,
areControlsVisible: true,
controlsTimeout: -1,
seekerOffset: 0,
seekerPosition: 0,
firstPlay: true
};
constructor(props) {
super(props);
this.state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'stretch',
duration: 0.0,
currentTime: 0.0,
paused: true,
fullscreenMode: false,
areControlsVisible: true,
controlsTimeout: -1,
seekerOffset: 0,
seekerPosition: 0,
firstPlay: true
};
}
formatTime(time) {
let str = '';
@ -144,10 +151,11 @@ class MediaPlayer extends React.PureComponent {
}
checkSeekerPosition(val = 0) {
if (val < 0) {
val = 0;
} else if (val >= this.seekerWidth) {
return this.seekerWidth;
const offset = this.getTrackingOffset();
if (val < offset) {
val = offset;
} else if (val >= (offset + this.seekerWidth)) {
return offset + this.seekerWidth;
}
return val;
@ -190,11 +198,18 @@ class MediaPlayer extends React.PureComponent {
});
}
getTrackingOffset() {
return this.state.fullscreenMode ? this.trackingOffset : 0;
}
getCurrentTimeForSeekerPosition() {
return this.state.duration * (this.state.seekerPosition / this.seekerWidth);
}
calculateSeekerPosition() {
if (this.state.fullscreenMode) {
return this.getTrackingOffset() + (this.seekerWidth * this.getCurrentTimePercentage());
}
return this.seekerWidth * this.getCurrentTimePercentage();
}
@ -209,6 +224,10 @@ class MediaPlayer extends React.PureComponent {
this.initSeeker();
}
componentDidMount() {
}
componentWillUnmount() {
this.clearControlsTimeout();
this.setState({ paused: true, fullscreenMode: false });
@ -243,13 +262,23 @@ class MediaPlayer extends React.PureComponent {
}
render() {
const { backgroundPlayEnabled, fileInfo, thumbnail, style, fullScreenStyle } = this.props;
const { backgroundPlayEnabled, fileInfo, thumbnail, style } = this.props;
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
let styles = [this.state.fullscreenMode ? mediaPlayerStyle.fullscreenContainer : mediaPlayerStyle.container];
if (style) {
if (style.length) {
styles = styles.concat(style);
} else {
styles.push(style);
}
}
const trackingStyle = [mediaPlayerStyle.trackingControls, this.state.fullscreenMode ?
mediaPlayerStyle.fullscreenTrackingControls : mediaPlayerStyle.containedTrackingControls];
return (
<View style={[style, mediaPlayerStyle.container]}>
<View style={mediaPlayerStyle.playerBackground} />
<View style={styles}>
<Video source={{ uri: 'file:///' + fileInfo.download_path }}
ref={(ref: Video) => { this.video = ref }}
resizeMode={this.state.resizeMode}
@ -267,12 +296,16 @@ class MediaPlayer extends React.PureComponent {
{this.renderPlayerControls()}
</TouchableOpacity>
<View style={mediaPlayerStyle.trackingControls}>
<View style={mediaPlayerStyle.progress} onLayout={(evt) => this.seekerWidth = evt.nativeEvent.layout.width}>
{(!this.state.fullscreenMode || (this.state.fullscreenMode && this.state.areControlsVisible)) &&
<View style={trackingStyle} onLayout={(evt) => {
this.trackingOffset = evt.nativeEvent.layout.x;
this.seekerWidth = evt.nativeEvent.layout.width;
}}>
<View style={mediaPlayerStyle.progress}>
<View style={[mediaPlayerStyle.innerProgressCompleted, { flex: flexCompleted }]} />
<View style={[mediaPlayerStyle.innerProgressRemaining, { flex: flexRemaining }]} />
</View>
</View>
</View>}
{this.state.areControlsVisible &&
<View style={[mediaPlayerStyle.seekerHandle, { left: this.state.seekerPosition }]} { ...this.seekResponder.panHandlers }>

View file

@ -20,15 +20,18 @@ import Video from 'react-native-video';
import filePageStyle from '../../styles/filePage';
class FilePage extends React.PureComponent {
state = {
mediaLoaded: false,
fullscreenMode: false
};
static navigationOptions = {
title: ''
};
constructor(props) {
super(props);
this.state = {
mediaLoaded: false,
fullscreenMode: false
};
}
componentDidMount() {
StatusBar.setHidden(false);
this.fetchFileInfo(this.props);
@ -128,27 +131,34 @@ class FilePage extends React.PureComponent {
const mediaType = Lbry.getMediaType(contentType);
const isPlayable = mediaType === 'video' || mediaType === 'audio';
const { height, channel_name: channelName, value } = claim;
const showActions = !this.state.fullscreenMode && (completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
const showActions = !this.state.fullscreenMode &&
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
const channelClaimId =
value && value.publisherSignature && value.publisherSignature.certificateId;
const playerStyle = [filePageStyle.player, this.state.fullscreenMode ?
filePageStyle.fullscreenPlayer : filePageStyle.containedPlayer];
const playerBgStyle = [filePageStyle.playerBackground, this.state.fullscreenMode ?
filePageStyle.fullscreenPlayerBackground : filePageStyle.containedPlayerBackground];
// at least 2MB (or the full download) before media can be loaded
const canLoadMedia = fileInfo && (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes); // 2MB = 1024*1024*2
const canLoadMedia = fileInfo &&
(fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes); // 2MB = 1024*1024*2
return (
<View style={filePageStyle.pageContainer}>
<View style={this.state.fullscreenMode ? filePageStyle.fullscreenMedia : filePageStyle.mediaContainer}>
<View style={filePageStyle.mediaContainer}>
{(!fileInfo || (isPlayable && !canLoadMedia)) &&
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}
{isPlayable && !this.state.mediaLoaded && <ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />}
{!completed && !canLoadMedia && <FileDownloadButton uri={navigation.state.params.uri} style={filePageStyle.downloadButton} />}
{canLoadMedia && <View style={filePageStyle.playerBackground} />}
{canLoadMedia && <MediaPlayer fileInfo={fileInfo}
</View>
{canLoadMedia && <View style={playerBgStyle} />}
{canLoadMedia && <MediaPlayer fileInfo={fileInfo}
uri={navigation.state.params.uri}
style={filePageStyle.player}
style={playerStyle}
onFullscreenToggled={this.handleFullscreenToggle}
onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>}
</View>
{ showActions &&
<View style={filePageStyle.actions}>
{completed && <Button color="red" title="Delete" onPress={this.onDeletePressed} />}

View file

@ -17,15 +17,6 @@ const filePageStyle = StyleSheet.create({
width: screenWidth,
height: 220
},
playerBackground: {
position: 'absolute',
flex: 1,
left: 0,
top: 0,
right: 0,
bottom: 16,
backgroundColor: Colors.Black
},
emptyClaimText: {
fontFamily: 'Metropolis-Regular',
textAlign: 'center',
@ -77,21 +68,37 @@ const filePageStyle = StyleSheet.create({
top: '40%'
},
player: {
flex: 1,
width: '100%',
height: '100%',
marginBottom: 14,
zIndex: 99
},
fullscreenMedia: {
position: 'absolute',
left: 0,
top: 0,
zIndex: 101
},
containedPlayer: {
width: '100%',
height: 220,
},
fullscreenPlayer: {
width: '100%',
height: '100%',
right: 0,
bottom: 0,
flex: 1,
backgroundColor: Colors.Black,
zIndex: 100
bottom: 0
},
playerBackground: {
position: 'absolute',
left: 0,
top: 0,
zIndex: 100,
backgroundColor: Colors.Black
},
containedPlayerBackground: {
width: '100%',
height: 206,
},
fullscreenPlayerBackground: {
width: '100%',
height: '100%',
right: 0,
bottom: 0
},
actions: {
paddingLeft: 16,

View file

@ -1,21 +1,29 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const mediaPlayerStyle = StyleSheet.create({
player: {
flex: 1
flex: 1,
width: '100%',
height: '100%'
},
container: {
marginBottom: 0,
paddingBottom: 16,
flex: 1,
paddingBottom: 16
},
fullscreenContainer: {
flex: 1,
justifyContent: 'center'
},
progress: {
flex: 1,
flexDirection: 'row',
overflow: 'hidden',
width: '100%',
height: 3
},
innerProgressCompleted: {
height: 4,
backgroundColor: '#40c0a9',
backgroundColor: Colors.LbryGreen,
},
innerProgressRemaining: {
height: 4,
@ -23,10 +31,16 @@ const mediaPlayerStyle = StyleSheet.create({
},
trackingControls: {
height: 3,
width: '100%',
position: 'absolute',
bottom: 14,
bottom: 14
},
containedTrackingControls: {
left: 0,
width: '100%'
},
fullscreenTrackingControls: {
alignSelf: 'center',
width: '70%'
},
playerControls: {
position: 'absolute',
@ -77,23 +91,24 @@ const mediaPlayerStyle = StyleSheet.create({
borderRadius: 12,
position: 'relative',
top: 14,
left: 8,
left: 15,
height: 12,
width: 12,
backgroundColor: '#40c0a9'
},
seekerHandle: {
backgroundColor: 'transparent',
position: 'absolute',
height: 36,
width: 36,
width: 48,
bottom: 0,
marginLeft: -8
marginLeft: -16
},
bigSeekerCircle: {
borderRadius: 24,
position: 'relative',
top: 8,
left: 8,
left: 15,
height: 24,
width: 24,
backgroundColor: '#40c0a9'