Unexpected behaviour using seek in react-native-video - react-native

In react-native-video, whenever I click on the (custom) progress bar on a value less than 50% (half of it), the video jumps to start instead of seeking to the right time. When I click above 50%, it goes to 50%. It's not actually 50, more like 55-60 but whatever. This is really weird, was not able to find anything online!
import Video from 'react-native-video';
import ProgressBar from "react-native-progress/Bar";
class Welcome extends React.Component {
player;
constructor(props) {
super(props)
//this.player = React.createRef();
this.state = {
paused: false,
loaded: false,
progress: 0,
duration: 0,
pressed:false,
screenType: 'contain',
};
console.log("--- Screen --- Welcome")
}
componentDidMount = () => {
setTimeout(() => {
this.player.seek(8)
},8000)
}
handleMainButtonTouch = () => {
console.log("inside handleMainButtonTouch")
console.log(this.state.progress)
if (this.state.progress >= 1) {
this.player.seek(0);
}
this.setState(state => {
return {
paused: !state.paused,
};
});
};
handleProgressPress = e => {
const position = e.nativeEvent.locationX;
const progress = parseFloat(position / 250) * this.state.duration;
const isPlaying = !this.state.paused;
this.player.seek(progress);
};
handleProgress = progress => {
this.setState({
progress: parseFloat(progress.currentTime) / parseFloat(this.state.duration),
});
};
handleEnd = () => {
this.setState({
paused: true ,
progress: 0 ,
});
this.player.seek(0);
};
handleLoad = meta => {
this.setState({
loaded: true,
duration: meta.duration,
});
};
handleFullScreen = () => {
if (this.state.screenType == 'contain')
this.setState({ screenType: 'cover' });
else this.setState({ screenType: 'contain' });
};
render() {
return (
<View style={styles.container}>
<View style={this.handleOuterViewStyle()}>
<Video
paused={this.state.paused}
source={{uri: "https://res.cloudinary.com/dy6bbey4u/video/upload/v1565532579/fam/videos/sample.mp4"}}
resizeMode={this.state.screenType}
onLoad={this.handleLoad}
onProgress={this.handleProgress}
onEnd={this.handleEnd}
ref={ref => {
this.player = ref;
}}
/>
{ this.state.loaded &&
<View style={styles.controls}>
<TouchableWithoutFeedback onPress={this.handleMainButtonTouch}>
<Text>Play</Text>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.handleProgressPress}>
<View>
<ProgressBar
animated={false}
progress={this.state.progress}
color="#FFF"
borderColor="#FFF"
width={250}
height={20}
/>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.handleFullScreen}>
<Text style={styles.fullscreenButton}>Full</Text>
</TouchableWithoutFeedback>
</View>
}
</View>
</View>
)
}
}
export default Welcome

I was also facing the same problem. Whenever i backward the video, it goes forward. The problem is depending on the video format. I was using the Webm format. Now the mp4 format solved the problem.
P.S. Sorry for the late reply.

ffmpeg -i input.mp4 -force_key_frames "expr:gte(t,n_forced*1)" output.mp4
Solved by forcing adding keyframes to the video.

So the solution for me was changing line 640 from android/src/main/java/com/brentvatne/react/ReactVideoView.java
- super.seekTo(msec);
+ mMediaPlayer.seekTo(msec,3);
Original response: https://github.com/react-native-video/react-native-video/issues/2230#issuecomment-892982288

Related

Styling camera on React native

On a screen, I want to scan tickets this way :
class Tickets extends Component {
constructor(props) {
super(props);
this.state = {
Press: false,
hasCameraPermission: null,
reference: '',
lastScannedUrl:null,
displayArray: []
};
}
initListData = async () => {
let list = await getProductByRef(1);
if (list) {
this.setState({
displayArray: list,
reference: list.reference
});
}
console.log('reference dans initListData =', list.reference)
};
async UNSAFE_componentWillMount() {
this.initListData();
console.log('reference dans le state =', this.state.reference)
};
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_Scan = () => {
this.setState({
Press: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ Press: false, scanned: true, reference: data });
this.props.navigation.navigate('ProductDetails', {reference : parseInt(this.state.state.reference)})
};
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>{i18n.t("scan.request")}</Text>;
}
if (hasCameraPermission === false) {
return <Text>{i18n.t("scan.noaccess")}</Text>;
}
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1, ...StyleSheet.absoluteFillObject}}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
render() {
const { hasCameraPermission, scanned, Press } = this.state;
let marker = null;
console.log('displayArray', this.state.displayArray, 'reference', this.state.displayArray.reference)
return (
<View style={{flex:1}}>
<KeyboardAvoidingView behavior="padding" enabled style={{flex:1}}>
<ScrollView contentContainerStyle={{flexGrow: 1 }} >
{Press ? (
<View style={{flex:1}}>
{this.renderBarcodeReader()}
</View>
) : (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button
color="#F78400"
title={i18n.t("scan.scan")}
onPress={this._onPress_Scan}>
</Button>
</View>
)}
</ScrollView>
</KeyboardAvoidingView>
</View>
);
}
}
export default Tickets;
This code gives me
As you can see I have a top and bottom margin. I would like there to be no space, for the camera to take the entire screen (and for any buttons to be displayed over the camera image)
How can I do it, the style of which element should I change?
Thanks for any help and explanations
can you leave your code for that part? now everything is okay but i believe the image width and height is static and you are not using resizeMode for that image, for camera it will be different .
you can check resizeMode for the camera library you are using

improving elements styles to make a full screen scan

I will need a helping hand to edit this page. i have all the elements but i need help styling.
I would like to have the camera (the image you see is the typical emulator camera, that's why it makes an image) in full screen and from above at the top, the message in red and the 'autocomplete.
If you want, to explain better, I would like to respect the image below: autocomplete at the top left above the camera in full screen.
would it be possible for you to help me, I'm getting a little confused. I tried to do a snack but failed. I will add it later if i can.
const autocompletes = [...Array(10).keys()];
const apiUrl = "https://5b927fd14c818e001456e967.mockapi.io/branches";
class Tickets extends Component {
constructor(props) {
super(props);
this.state = {
Press: false,
hasCameraPermission: null,
reference: '',
lastScannedUrl:null,
displayArray: []
};
}
initListData = async () => {
let list = await getProductByRef(1);
if (list) {
this.setState({
displayArray: list,
reference: list.reference
});
}
// console.log('reference dans initListData =', list.reference)
};
async UNSAFE_componentWillMount() {
this.initListData();
// console.log('reference dans le state =', this.state.reference)
};
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_Scan = () => {
this.setState({
Press: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ Press: false, scanned: true, reference: data });
this.props.navigation.navigate('ProductDetails', {reference : parseInt(this.state.state.reference)})
};
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>{i18n.t("scan.request")}</Text>;
}
if (hasCameraPermission === false) {
return <Text>{i18n.t("scan.noaccess")}</Text>;
}
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1, height:'100%', ...StyleSheet.absoluteFillObject}}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
handleSelectItem(item, index) {
const {onDropdownClose} = this.props;
onDropdownClose();
console.log(item);
}
render() {
const { hasCameraPermission, scanned, Press } = this.state;
let marker = null;
const {scrollToInput, onDropdownClose, onDropdownShow} = this.props;
// console.log('displayArray', this.state.displayArray, 'reference', this.state.displayArray.reference)
return (
<View style={styles.container}>
{Press ? (
<View style={{flex:1}}>
<View style={styles.dropdownContainerStyle}>
<Autocomplete
key={shortid.generate()}
containerStyle={styles.autocompleteContainer}
inputStyle={{ borderWidth: 1, borderColor: '#F78400'}}
placeholder={i18n.t("tickets.warning")}
pickerStyle={styles.autocompletePicker}
scrollStyle={styles.autocompleteScroll}
scrollToInput={ev => scrollToInput(ev)}
handleSelectItem={(item, id) => this.handleSelectItem(item, id)}
onDropdownClose={() => onDropdownClose()}
onDropdownShow={() => onDropdownShow()}
fetchDataUrl={apiUrl}
minimumCharactersCount={2}
highlightText
valueExtractor={item => item.name}
rightContent
rightTextExtractor={item => item.properties}
/>
</View>
{this.renderBarcodeReader()}
</View>
) : (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button
color="#F78400"
title={i18n.t("scan.scan")}
onPress={this._onPress_Scan}>
</Button>
</View>
)}
</View>
);
}
}
export default Tickets;
This gives me (after pressing the button) :
SNACK CODE TEST
I notice You are using a component from Expo called BarCodeScanner
There's a github issue open about the fact that this component is not possible to be styled for full screen: https://github.com/expo/expo/issues/5212
However one user proposes a good solution: replace BarCodeScanner with Camera and use barcodescannersettings
Here's a link for the answer on the gitHub issue: https://github.com/expo/expo/issues/5212#issuecomment-653478266
Your code should look something like:
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
[ ... ] // the rest of your code here
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<Camera
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1}}
barCodeScannerSettings={{
barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
/>
</View>
);
}

How to show the image which is picked with react native image picker?

Problem:
I have created an image and video picker with react-native-image-picker. This is how my code looks like.
import React, {useState, createRef} from 'react';
import {
View,
TouchableOpacity,
Image,
TouchableWithoutFeedback,
} from 'react-native';
import AppText from '_components/appText';
import Icon from 'react-native-vector-icons/AntDesign';
import {strings} from '_translations/i18n';
import styles from './newpatientassetmentstyle';
import PlayerControls from '_components/playerControls';
import Video from 'react-native-video';
import ImagePicker from 'react-native-image-picker';
const showControls = (state, setState) => {
state.showControls
? setState({...state, showControls: false})
: setState({...state, showControls: true});
};
const handlePlayPause = (state, setState) => {
if (state.play) {
setState({...state, play: false, showControls: true});
return;
}
setState({...state, play: true});
setTimeout(() => setState((s) => ({...s, showControls: false})), 2000);
};
function onLoadEnd(data, state, setState) {
setState({
...state,
duration: data.duration,
currentTime: data.currentTime,
});
}
function onProgress(data, state, setState) {
setState({
...state,
currentTime: data.currentTime,
});
}
const onEnd = (state, setState, player) => {
setState({
...state,
showControls: false,
play: false,
currentTime: 0,
duration: 0,
});
player.current.seek(0);
};
const openPicker = async (type, setFileObject) => {
let options;
if (type === 4) {
options = {
title: 'Upload Image',
quality: 1,
mediaType: 'photo',
storageOptions: {
skipBackup: true,
path: 'images',
},
};
} else if (type === 5) {
options = {
title: 'Upload Video',
videoQuality: 'high',
mediaType: 'video',
storageOptions: {
skipBackup: true,
path: 'images',
},
};
}
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
setFileObject(response);
}
});
};
const DocumentUpload = (props) => {
const {type} = props;
const [fileObject, setFileObject] = useState(null);
const [state, setState] = useState({
fullscreen: false,
play: false,
currentTime: 0,
duration: 0,
showControls: true,
});
const player = createRef();
return (
<View>
{type === 5 && (
<View style={styles.videoContainer}>
<View style={styles.videoInnerContainer}>
<TouchableWithoutFeedback
onPress={() => showControls(state, setState)}>
<View style={{flex: 1}}>
<Video
source={{
uri: fileObject.uri,
}}
controls={false}
style={styles.backgroundVideo}
ref={player}
resizeMode={'contain'}
paused={!state.play}
onEnd={() => onEnd(state, setState, player)}
onLoad={(data) => onLoadEnd(data, state, setState)}
onProgress={(data) => onProgress(data, state, setState)}
/>
{state.showControls && (
<View style={styles.controlsOverlay}>
<PlayerControls
play={state.play}
playVideo={handlePlayPause}
state={state}
setState={setState}
pauseVideo={handlePlayPause}
/>
</View>
)}
</View>
</TouchableWithoutFeedback>
</View>
</View>
)}
{fileObject
? console.log(`data:${fileObject.type},${fileObject.data}`, 'fileOb')
: null}
{type === 4 && fileObject && (
<View>
<Image
source={{uri: 'data:' + fileObject.type + ',' + fileObject.data}}
/>
</View>
)}
{!fileObject ? (
<>
<TouchableOpacity onPress={() => openPicker(type, setFileObject)}>
<Image
source={require('_assets/img/cmerap.png')}
resizeMode="center"
style={styles.camPImage}
/>
</TouchableOpacity>
<AppText styles={styles.camPText}>
{strings('assetsment.capture')}
</AppText>
</>
) : (
<View style={styles.videoBottomText}>
<TouchableOpacity onPress={() => openPicker(type, setFileObject)}>
<View style={styles.updateAgainContainer}>
<Icon name="reload1" style={styles.reloadIcon} />
<AppText styles={styles.reloadText}>
{strings('assetsment.reload')}
</AppText>
</View>
</TouchableOpacity>
</View>
)}
</View>
);
};
export default DocumentUpload;
But when I try to show the picked image in the view but it is showing nothing in there. I tried a lot to find out what is wrong with this but I was unable to do so. Can someone help me to find out a solution to this problem? Thank you very much.
Once you have the image selected you'll have a response object, in which you'll have the uri for the image chosen (you can console.log the response object to make sure what the key for the uri is). Once you have this object with the response, use its uri to set a state and make sure you use that state in your image tag, like this: source={{ uri: imageUriFromState }}

How to refresh/re-render flatlist on react-native?

im trying to refresh my flatlist from some page without going back to the principal menu, but it doesnt work.
I've already readed about extraData, but it doesnt work either.
Basiclly my program is like that:
I have a page called "passwords" and i add some passwords there from another page called "add passwords". When i click to add a password, i want to refresh the flatlist from the page "passwords" to show me the password that i just added.
This is my code from the page "add passwords"
...
state = {
arr: [],
local: '',
password: '',
obj: {
local: '',
password: ''
},
count: 1,
texto: ''
};
componentDidMount() {
//Here is the Trick
const { navigation } = this.props;
//Adding an event listner om focus
//So whenever the screen will have focus it will set the state to zero
this.focusListener = navigation.addListener('didFocus', () => {
this.setState({ count: 0 });
});
}
storeItem(item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value~
console.log(item)
var joined = this.state.arr.concat(item);
console.log(joined)
this.setState({ arr: joined })
AsyncStorage.setItem('array', JSON.stringify(joined));
console.log(this.state.arr)
} catch (error) {
console.log(error.message);
}
}
componentWillMount() {
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
array ? this.setState({ arr: item }) : null;
console.log(item)
})
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ local: text })}
value={this.state.local}
/>
<TextInput
secureTextEntry={true}
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ password: text })}
value={this.state.password}
/>
<Button title='Adicionar'
onPress={() => this.storeItem({ local: this.state.local, password: this.state.password }) + alert("Adicionado com sucesso!") + this.props.navigation.navigate('Passwords')}
></Button>
</View>
);
}
}
And this is my page "passwords" where i want to refresh
componentWillMount() {
const { navigation } = this.props;
this.willFocusListener = navigation.addListener(
'willFocus',
() => {
this.setState({ count: 10 })
}
)
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
})
}
renderItem = ({ item }) => (
<View style={{ flexDirection: 'row' }} style={styles.passwordContainer}>
<Text> {item.local} </Text>
<Text> {item.password} </Text>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.arr}
renderItem={this.renderItem}
extraData={this.state} //this is what i tryied
/>
</View>
);
You can use your listener to update the state.
componentWillMount() {
this.willFocusListener = navigation.addListener(
'willFocus',
() => this.updateData()
}
updateData = () => {
this.setState({ count: 10 });
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
});
}
Any state changes will rerender items.

How to get ref in flat list item onpress?

I am trying to capture screen with react-native-view-shot. On press this.refs.viewShot.capture showing undefined.
Here is my code
Flat list code:
<FlatList
ref={(list) => this.myFlatList = list}
data={this.state.newsListArray}
keyExtractor={this._keyExtractor}
renderItem={this.renderRowItem}
/>
render on press link:
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos('5c63f7307518134a2aa288ce') :
this._captureScreenAndroid('5c63f7307518134a2aa288ce')
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
And that's the function:
_captureScreenIos = (refId) => {
console.log("Clicked for IOS");
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = 'viewShot-5c63f7307518134a2aa288ce';
this.myFlatList.viewShot.capture({width: 2048 / PixelRatio.get(), height: 2048 / PixelRatio.get()}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
Please let me know if anyone having a solution for the same.
**This is my app screen **
It's blur when we have long list items.
Try this:
import { captureRef } from react-native-view-shot
constructor(props) {
super(props);
this.refs = {};
}
renderItem = ({item, index}) => (
<TouchableOpacity
onPress={ () => {
captureRef(this.refs[`${index}`], options).then(.....)
}
>
<View
style={{flexDirection:'row'}}
ref={shot => this.refs[`${index}`] = shot}
>
...........
</View>
</TouchableOpacity>
)
React Native View Shot
I hope it help you.
That is a good amount of code. Try https://reactnativecode.com/take-screenshot-of-app-programmatically/
setting the state and try passing in the object you are referencing.
export default class App extends Component {
constructor(){
super();
this.state={
imageURI : 'https://reactnativecode.com/wp-content/uploads/2018/02/motorcycle.jpg'
}
}
captureScreenFunction=()=>{
captureScreen({
format: "jpg",
quality: 0.8
})
.then(
uri => this.setState({ imageURI : uri }),
error => console.error("Oops, Something Went Wrong", error)
);
}
Here is answer:
constructor(props) {
this.screenshot = {};
}
This is my function:
_captureScreenIos(itemId) {
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = itemId;
captureRef(this.screenshot[itemId],{format: 'jpg',quality: 0.8}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
This is the view:
<View collapsable={false} ref={(shot) => { this.screenshot[itemId] = shot; }} >
//some content here
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos(itemData.item._id) :
this._captureScreenAndroid(itemData.item._id)
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
</View>