Merge pull request #33 from lbryio/media-player
implemented rudimentary media seeker. Still needs some tweaking.
This commit is contained in:
commit
c6e0c6b39a
24 changed files with 2578 additions and 626 deletions
|
@ -20,7 +20,8 @@ const discoverStack = StackNavigator({
|
|||
File: {
|
||||
screen: FilePage,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
header: null,
|
||||
drawerLockMode: 'locked-closed'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
|
|
@ -58,7 +58,7 @@ class FileItem extends React.PureComponent {
|
|||
this.props.navigation.navigate('File', { uri: uri });
|
||||
}
|
||||
}>
|
||||
<FileItemMedia title={title} thumbnail={thumbnail} />
|
||||
<FileItemMedia title={title} thumbnail={thumbnail} resizeMode="cover" />
|
||||
<FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />
|
||||
<Text style={discoverStyle.fileItemName}>{title}</Text>
|
||||
{channelName &&
|
||||
|
|
|
@ -28,7 +28,7 @@ class FileItemMedia extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
let style = this.props.style;
|
||||
const { title, thumbnail } = this.props;
|
||||
const { title, thumbnail, resizeMode } = this.props;
|
||||
const atStyle = this.state.autoThumbStyle;
|
||||
|
||||
if (thumbnail && ((typeof thumbnail) === 'string')) {
|
||||
|
@ -37,7 +37,7 @@ class FileItemMedia extends React.PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<Image source={{uri: thumbnail}} resizeMode="cover" style={style} />
|
||||
<Image source={{uri: thumbnail}} resizeMode={resizeMode ? resizeMode : "cover"} style={style} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
7
app/src/component/mediaPlayer/index.js
Normal file
7
app/src/component/mediaPlayer/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import MediaPlayer from './view';
|
||||
|
||||
const select = state => ({});
|
||||
const perform = dispatch => ({});
|
||||
|
||||
export default connect(select, perform)(MediaPlayer);
|
273
app/src/component/mediaPlayer/view.js
Normal file
273
app/src/component/mediaPlayer/view.js
Normal file
|
@ -0,0 +1,273 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { PanResponder, Text, View, ScrollView, TouchableOpacity } from 'react-native';
|
||||
import Video from 'react-native-video';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import FileItemMedia from '../fileItemMedia';
|
||||
import mediaPlayerStyle from '../../styles/mediaPlayer';
|
||||
|
||||
class MediaPlayer extends React.PureComponent {
|
||||
static ControlsTimeout = 3000;
|
||||
|
||||
seekResponder = null;
|
||||
|
||||
seekerWidth = 0;
|
||||
|
||||
video = null;
|
||||
|
||||
state = {
|
||||
rate: 1,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
resizeMode: 'stretch',
|
||||
duration: 0.0,
|
||||
currentTime: 0.0,
|
||||
paused: false,
|
||||
fullscreenMode: false,
|
||||
areControlsVisible: true,
|
||||
controlsTimeout: -1,
|
||||
seekerOffset: 0,
|
||||
seekerPosition: 0,
|
||||
firstPlay: true
|
||||
};
|
||||
|
||||
formatTime(time) {
|
||||
let str = '';
|
||||
let minutes = 0, hours = 0, seconds = parseInt(time, 10);
|
||||
if (seconds > 60) {
|
||||
minutes = parseInt(seconds / 60, 10);
|
||||
seconds = seconds % 60;
|
||||
|
||||
if (minutes > 60) {
|
||||
hours = parseInt(minutes / 60, 10);
|
||||
minutes = minutes % 60;
|
||||
}
|
||||
|
||||
str = (hours > 0 ? this.pad(hours) + ':' : '') + this.pad(minutes) + ':' + this.pad(seconds);
|
||||
} else {
|
||||
str = '00:' + this.pad(seconds);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
pad(value) {
|
||||
if (value < 10) {
|
||||
return '0' + String(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
onLoad = (data) => {
|
||||
this.setState({
|
||||
duration: data.duration
|
||||
});
|
||||
if (this.props.onMediaLoaded) {
|
||||
this.props.onMediaLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
onProgress = (data) => {
|
||||
this.setState({ currentTime: data.currentTime });
|
||||
|
||||
if (!this.state.seeking) {
|
||||
this.setSeekerPosition(this.calculateSeekerPosition());
|
||||
}
|
||||
|
||||
if (this.state.firstPlay) {
|
||||
this.setState({ firstPlay: false });
|
||||
this.hidePlayerControls();
|
||||
}
|
||||
}
|
||||
|
||||
clearControlsTimeout = () => {
|
||||
if (this.state.controlsTimeout > -1) {
|
||||
clearTimeout(this.state.controlsTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
showPlayerControls = () => {
|
||||
this.clearControlsTimeout();
|
||||
if (!this.state.areControlsVisible) {
|
||||
this.setState({ areControlsVisible: true });
|
||||
}
|
||||
this.hidePlayerControls();
|
||||
}
|
||||
|
||||
hidePlayerControls() {
|
||||
const player = this;
|
||||
let timeout = setTimeout(() => {
|
||||
player.setState({ areControlsVisible: false });
|
||||
}, MediaPlayer.ControlsTimeout);
|
||||
player.setState({ controlsTimeout: timeout });
|
||||
}
|
||||
|
||||
togglePlay = () => {
|
||||
this.showPlayerControls();
|
||||
this.setState({ paused: !this.state.paused });
|
||||
}
|
||||
|
||||
toggleFullscreenMode = () => {
|
||||
this.showPlayerControls();
|
||||
const { onFullscreenToggled } = this.props;
|
||||
this.setState({ fullscreenMode: !this.state.fullscreenMode }, () => {
|
||||
this.setState({ resizeMode: this.state.fullscreenMode ? 'contain' : 'stretch' });
|
||||
if (onFullscreenToggled) {
|
||||
onFullscreenToggled(this.state.fullscreenMode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onEnd = () => {
|
||||
this.setState({ paused: true });
|
||||
this.video.seek(0);
|
||||
}
|
||||
|
||||
setSeekerPosition(position = 0) {
|
||||
position = this.checkSeekerPosition(position);
|
||||
this.setState({ seekerPosition: position });
|
||||
if (!this.state.seeking) {
|
||||
this.setState({ seekerOffset: position });
|
||||
}
|
||||
}
|
||||
|
||||
checkSeekerPosition(val = 0) {
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
} else if (val >= this.seekerWidth) {
|
||||
return this.seekerWidth;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
seekTo = (time = 0) => {
|
||||
if (time > this.state.duration) {
|
||||
return;
|
||||
}
|
||||
this.video.seek(time);
|
||||
this.setState({ currentTime: time });
|
||||
}
|
||||
|
||||
initSeeker() {
|
||||
this.seekResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: (evt, gestureState) => true,
|
||||
onMoveShouldSetPanResponder: (evt, gestureState) => true,
|
||||
|
||||
onPanResponderGrant: (evt, gestureState) => {
|
||||
this.clearControlsTimeout();
|
||||
this.setState({ seeking: true });
|
||||
},
|
||||
|
||||
onPanResponderMove: (evt, gestureState) => {
|
||||
const position = this.state.seekerOffset + gestureState.dx;
|
||||
this.setSeekerPosition(position);
|
||||
},
|
||||
|
||||
onPanResponderRelease: (evt, gestureState) => {
|
||||
const time = this.getCurrentTimeForSeekerPosition();
|
||||
if (time >= this.state.duration) {
|
||||
this.setState({ paused: true });
|
||||
this.onEnd();
|
||||
} else {
|
||||
this.seekTo(time);
|
||||
this.setState({ seeking: false });
|
||||
}
|
||||
this.hidePlayerControls();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentTimeForSeekerPosition() {
|
||||
return this.state.duration * (this.state.seekerPosition / this.seekerWidth);
|
||||
}
|
||||
|
||||
calculateSeekerPosition() {
|
||||
return this.seekerWidth * this.getCurrentTimePercentage();
|
||||
}
|
||||
|
||||
getCurrentTimePercentage() {
|
||||
if (this.state.currentTime > 0) {
|
||||
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.initSeeker();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.setState({ paused: true, fullscreenMode: false });
|
||||
const { onFullscreenToggled } = this.props;
|
||||
if (onFullscreenToggled) {
|
||||
onFullscreenToggled(false);
|
||||
}
|
||||
}
|
||||
|
||||
renderPlayerControls() {
|
||||
if (this.state.areControlsVisible) {
|
||||
return (
|
||||
<View style={mediaPlayerStyle.playerControlsContainer}>
|
||||
<TouchableOpacity style={mediaPlayerStyle.playPauseButton}
|
||||
onPress={this.togglePlay}>
|
||||
{this.state.paused && <Icon name="play" size={32} color="#ffffff" />}
|
||||
{!this.state.paused && <Icon name="pause" size={32} color="#ffffff" />}
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={mediaPlayerStyle.toggleFullscreenButton} onPress={this.toggleFullscreenMode}>
|
||||
{this.state.fullscreenMode && <Icon name="compress" size={16} color="#ffffff" />}
|
||||
{!this.state.fullscreenMode && <Icon name="expand" size={16} color="#ffffff" />}
|
||||
</TouchableOpacity>
|
||||
|
||||
<Text style={mediaPlayerStyle.elapsedDuration}>{this.formatTime(this.state.currentTime)}</Text>
|
||||
<Text style={mediaPlayerStyle.totalDuration}>{this.formatTime(this.state.duration)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { fileInfo, title, thumbnail, style, fullScreenStyle } = this.props;
|
||||
const flexCompleted = this.getCurrentTimePercentage() * 100;
|
||||
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
|
||||
|
||||
return (
|
||||
<View style={[style, mediaPlayerStyle.container]}>
|
||||
<Video source={{ uri: 'file:///' + fileInfo.download_path }}
|
||||
ref={(ref: Video) => { this.video = ref }}
|
||||
resizeMode={this.state.resizeMode}
|
||||
playInBackground={true}
|
||||
style={mediaPlayerStyle.player}
|
||||
rate={this.state.rate}
|
||||
volume={this.state.volume}
|
||||
paused={this.state.paused}
|
||||
onLoad={this.onLoad}
|
||||
onProgress={this.onProgress}
|
||||
onEnd={this.onEnd}
|
||||
/>
|
||||
|
||||
<TouchableOpacity style={mediaPlayerStyle.playerControls} onPress={this.showPlayerControls}>
|
||||
{this.renderPlayerControls()}
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={mediaPlayerStyle.trackingControls}>
|
||||
<View style={mediaPlayerStyle.progress} onLayout={(evt) => this.seekerWidth = evt.nativeEvent.layout.width}>
|
||||
<View style={[mediaPlayerStyle.innerProgressCompleted, { flex: flexCompleted }]} />
|
||||
<View style={[mediaPlayerStyle.innerProgressRemaining, { flex: flexRemaining }]} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{this.state.areControlsVisible &&
|
||||
<View style={[mediaPlayerStyle.seekerHandle, { left: this.state.seekerPosition }]} { ...this.seekResponder.panHandlers }>
|
||||
<View style={this.state.seeking ? mediaPlayerStyle.bigSeekerCircle : mediaPlayerStyle.seekerCircle} />
|
||||
</View>}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MediaPlayer;
|
|
@ -1,20 +1,16 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { Text, View, ScrollView, TouchableOpacity } from 'react-native';
|
||||
import Video from 'react-native-video';
|
||||
import filePageStyle from '../../styles/filePage';
|
||||
import { Text, View, ScrollView, StatusBar, TouchableOpacity, NativeModules } from 'react-native';
|
||||
import FileItemMedia from '../../component/fileItemMedia';
|
||||
import FileDownloadButton from '../../component/fileDownloadButton';
|
||||
import MediaPlayer from '../../component/mediaPlayer';
|
||||
import Video from 'react-native-video';
|
||||
import filePageStyle from '../../styles/filePage';
|
||||
|
||||
class FilePage extends React.PureComponent {
|
||||
state = {
|
||||
rate: 1,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
resizeMode: 'contain',
|
||||
duration: 0.0,
|
||||
currentTime: 0.0,
|
||||
paused: true,
|
||||
mediaLoaded: false,
|
||||
fullscreenMode: false
|
||||
};
|
||||
|
||||
static navigationOptions = {
|
||||
|
@ -22,6 +18,7 @@ class FilePage extends React.PureComponent {
|
|||
};
|
||||
|
||||
componentDidMount() {
|
||||
StatusBar.setHidden(false);
|
||||
this.fetchFileInfo(this.props);
|
||||
this.fetchCostInfo(this.props);
|
||||
}
|
||||
|
@ -42,6 +39,26 @@ class FilePage extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleFullscreenToggle = (mode) => {
|
||||
this.setState({ fullscreenMode: mode });
|
||||
StatusBar.setHidden(mode);
|
||||
if (NativeModules.ScreenOrientation) {
|
||||
if (mode) {
|
||||
// fullscreen, so change orientation to landscape mode
|
||||
NativeModules.ScreenOrientation.lockOrientationLandscape();
|
||||
} else {
|
||||
NativeModules.ScreenOrientation.unlockOrientation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
StatusBar.setHidden(false);
|
||||
if (NativeModules.ScreenOrientation) {
|
||||
NativeModules.ScreenOrientation.unlockOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
claim,
|
||||
|
@ -73,25 +90,14 @@ class FilePage extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<View style={filePageStyle.pageContainer}>
|
||||
<View style={filePageStyle.mediaContainer}>
|
||||
{(!fileInfo || !isPlayable) && <FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}
|
||||
<View style={this.state.fullscreenMode ? filePageStyle.fullscreenMedia : filePageStyle.mediaContainer}>
|
||||
{(!fileInfo || (isPlayable && !this.state.mediaLoaded)) &&
|
||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}
|
||||
{!completed && <FileDownloadButton uri={navigation.state.params.uri} style={filePageStyle.downloadButton} />}
|
||||
|
||||
{fileInfo && isPlayable &&
|
||||
<TouchableOpacity
|
||||
style={filePageStyle.player}
|
||||
onPress={() => this.setState({ paused: !this.state.paused })}>
|
||||
<Video source={{ uri: 'file:///' + fileInfo.download_path }}
|
||||
resizeMode="cover"
|
||||
playInBackground={true}
|
||||
style={filePageStyle.player}
|
||||
rate={this.state.rate}
|
||||
volume={this.state.volume}
|
||||
paused={this.state.paused}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
|
||||
{fileInfo && isPlayable && <MediaPlayer fileInfo={fileInfo}
|
||||
style={filePageStyle.player}
|
||||
onFullscreenToggled={this.handleFullscreenToggle}
|
||||
onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>}
|
||||
</View>
|
||||
<ScrollView style={filePageStyle.scrollContainer}>
|
||||
<Text style={filePageStyle.title}>{title}</Text>
|
||||
|
|
0
app/src/page/settings/index.js
Normal file
0
app/src/page/settings/index.js
Normal file
0
app/src/page/settings/view.js
Normal file
0
app/src/page/settings/view.js
Normal file
|
@ -9,11 +9,13 @@ const discoverStyle = StyleSheet.create({
|
|||
flex: 1
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
categoryName: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 20,
|
||||
marginLeft: 24,
|
||||
marginTop: 16,
|
||||
|
@ -26,15 +28,15 @@ const discoverStyle = StyleSheet.create({
|
|||
marginBottom: 48
|
||||
},
|
||||
fileItemName: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
marginTop: 8,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
fontSize: 16
|
||||
},
|
||||
channelName: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 14,
|
||||
marginTop: 4,
|
||||
color: '#c0c0c0',
|
||||
fontWeight: 'bold'
|
||||
color: '#c0c0c0'
|
||||
},
|
||||
filePriceContainer: {
|
||||
backgroundColor: '#61fcd8',
|
||||
|
@ -47,10 +49,10 @@ const discoverStyle = StyleSheet.create({
|
|||
borderRadius: 4
|
||||
},
|
||||
filePriceText: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
color: '#0c604b',
|
||||
fontWeight: 'bold'
|
||||
color: '#0c604b'
|
||||
},
|
||||
drawerHamburger: {
|
||||
marginLeft: 8
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const fileDownloadButtonStyle = StyleSheet.create({
|
||||
container: {
|
||||
width: 120,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#40c0a9',
|
||||
},
|
||||
text: {
|
||||
color: '#ffffff',
|
||||
fontSize: 13,
|
||||
textAlign: 'center'
|
||||
}
|
||||
container: {
|
||||
width: 120,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#40c0a9',
|
||||
},
|
||||
text: {
|
||||
fontFamily: 'Metropolis-Medium',
|
||||
color: '#ffffff',
|
||||
fontSize: 14,
|
||||
textAlign: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
export default fileDownloadButtonStyle;
|
|
@ -5,11 +5,13 @@ const width = screenDimension.width - 48; // screen width minus combined left an
|
|||
|
||||
const fileItemMediaStyle = StyleSheet.create({
|
||||
autothumb: {
|
||||
width: width,
|
||||
height: 180,
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
height: 200,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
autothumbText: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
textAlign: 'center',
|
||||
color: '#ffffff',
|
||||
fontSize: 40
|
||||
|
@ -48,8 +50,9 @@ const fileItemMediaStyle = StyleSheet.create({
|
|||
backgroundColor: '#ffa726'
|
||||
},
|
||||
thumbnail: {
|
||||
width: width,
|
||||
height: 180,
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
height: 200,
|
||||
shadowColor: 'transparent'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -12,10 +12,12 @@ const filePageStyle = StyleSheet.create({
|
|||
flex: 1
|
||||
},
|
||||
mediaContainer: {
|
||||
backgroundColor: '#000000',
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
width: screenWidth,
|
||||
height: 220,
|
||||
},
|
||||
emptyClaimText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
textAlign: 'center',
|
||||
fontSize: 20,
|
||||
marginLeft: 16,
|
||||
|
@ -25,22 +27,23 @@ const filePageStyle = StyleSheet.create({
|
|||
flex: 1
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 20,
|
||||
marginTop: 12,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
marginBottom: 12
|
||||
},
|
||||
channelName: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
marginBottom: 20,
|
||||
color: '#9b9b9b'
|
||||
},
|
||||
description: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 16,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
|
@ -56,8 +59,20 @@ const filePageStyle = StyleSheet.create({
|
|||
top: '50%'
|
||||
},
|
||||
player: {
|
||||
width: screenWidth,
|
||||
height: 200
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
marginBottom: 14
|
||||
},
|
||||
fullscreenMedia: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
backgroundColor: '#000000',
|
||||
zIndex: 100
|
||||
}
|
||||
});
|
||||
|
||||
|
|
104
app/src/styles/mediaPlayer.js
Normal file
104
app/src/styles/mediaPlayer.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
import { StyleSheet, Dimensions } from 'react-native';
|
||||
|
||||
const screenDimension = Dimensions.get('window');
|
||||
const screenWidth = screenDimension.width;
|
||||
|
||||
const mediaPlayerStyle = StyleSheet.create({
|
||||
player: {
|
||||
flex: 1
|
||||
},
|
||||
container: {
|
||||
},
|
||||
progress: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
innerProgressCompleted: {
|
||||
height: 4,
|
||||
backgroundColor: '#40c0a9',
|
||||
},
|
||||
innerProgressRemaining: {
|
||||
height: 4,
|
||||
backgroundColor: '#2c2c2c',
|
||||
},
|
||||
trackingControls: {
|
||||
height: 3,
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
},
|
||||
playerControls: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
playerControlsContainer: {
|
||||
backgroundColor: 'transparent',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
playPauseButton: {
|
||||
position: 'absolute',
|
||||
width: 64,
|
||||
height: 64,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
toggleFullscreenButton: {
|
||||
position: 'absolute',
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
right: 0,
|
||||
bottom: 14,
|
||||
},
|
||||
elapsedDuration: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
position: 'absolute',
|
||||
left: 8,
|
||||
bottom: 24,
|
||||
fontSize: 12,
|
||||
color: '#ffffff'
|
||||
},
|
||||
totalDuration: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
position: 'absolute',
|
||||
right: 40,
|
||||
bottom: 24,
|
||||
fontSize: 12,
|
||||
color: '#ffffff'
|
||||
},
|
||||
seekerCircle: {
|
||||
borderRadius: 12,
|
||||
position: 'relative',
|
||||
top: 8,
|
||||
left: 8,
|
||||
height: 12,
|
||||
width: 12,
|
||||
backgroundColor: '#40c0a9'
|
||||
},
|
||||
seekerHandle: {
|
||||
position: 'absolute',
|
||||
height: 28,
|
||||
width: 28,
|
||||
bottom: -12,
|
||||
marginLeft: -8
|
||||
},
|
||||
bigSeekerCircle: {
|
||||
borderRadius: 24,
|
||||
position: 'relative',
|
||||
top: 2,
|
||||
left: 8,
|
||||
height: 24,
|
||||
width: 24,
|
||||
backgroundColor: '#40c0a9'
|
||||
}
|
||||
});
|
||||
|
||||
export default mediaPlayerStyle;
|
|
@ -7,13 +7,14 @@ const splashStyle = StyleSheet.create({
|
|||
backgroundColor: '#40b89a'
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 64,
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
marginBottom: 48,
|
||||
color: '#ffffff'
|
||||
},
|
||||
details: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
|
@ -21,7 +22,7 @@ const splashStyle = StyleSheet.create({
|
|||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 18,
|
||||
color: '#ffffff',
|
||||
marginLeft: 16,
|
||||
|
|
BIN
src/main/assets/fonts/FontAwesome.ttf
Normal file
BIN
src/main/assets/fonts/FontAwesome.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/Metropolis-Bold.ttf
Normal file
BIN
src/main/assets/fonts/Metropolis-Bold.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/Metropolis-Medium.ttf
Normal file
BIN
src/main/assets/fonts/Metropolis-Medium.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/Metropolis-Regular.ttf
Normal file
BIN
src/main/assets/fonts/Metropolis-Regular.ttf
Normal file
Binary file not shown.
BIN
src/main/assets/fonts/Metropolis-SemiBold.ttf
Normal file
BIN
src/main/assets/fonts/Metropolis-SemiBold.ttf
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -1 +1 @@
|
|||
·ѓ*Цї<D0A6>1rЏ8Љ¤)Чт»EгЉ
|
||||
WhèÑ'ï¢Ã,š|!ÚÊ.ÔÿòR
|
|
@ -18,7 +18,7 @@ import java.util.Random;
|
|||
* Created by akinwale on 3/15/18.
|
||||
*/
|
||||
|
||||
public class LbryDownloadManagerModule extends ReactContextBaseJavaModule {
|
||||
public class DownloadManagerModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
private HashMap<Integer, NotificationCompat.Builder> builders = new HashMap<Integer, NotificationCompat.Builder>();
|
||||
|
@ -29,7 +29,7 @@ public class LbryDownloadManagerModule extends ReactContextBaseJavaModule {
|
|||
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
|
||||
|
||||
public LbryDownloadManagerModule(ReactApplicationContext reactContext) {
|
||||
public DownloadManagerModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.lbry.lbrynet.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
/**
|
||||
* Created by akinwale on 3/19/18.
|
||||
*/
|
||||
|
||||
public class ScreenOrientationModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
public ScreenOrientationModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ScreenOrientation";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void unlockOrientation() {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void lockOrientationLandscape() {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@ import com.facebook.react.bridge.NativeModule;
|
|||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import io.lbry.lbrynet.reactmodules.LbryDownloadManagerModule;
|
||||
import io.lbry.lbrynet.reactmodules.DownloadManagerModule;
|
||||
import io.lbry.lbrynet.reactmodules.ScreenOrientationModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -21,8 +22,9 @@ public class LbryReactPackage implements ReactPackage {
|
|||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new LbryDownloadManagerModule(reactContext));
|
||||
|
||||
modules.add(new DownloadManagerModule(reactContext));
|
||||
modules.add(new ScreenOrientationModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue