In React Native how can I access the native camera app? - react-native

I would like to add a link to my app that opens the phone's native camera app? Is this possible?
I'm aware that react-native-camera exists but from the docs it seems like it only supports accessing the camera for the purpose of creating your own camera interface inside your app. I would rather just use the camera app already on the phone.
Thank you

Use react-native-image-picker, you can access phone native's camera.
[EDIT]
An example Code
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image,
Button
} from 'react-native';
import ImagePicker from "react-native-image-picker";
export default class App extends Component {
state = {
pickedImage: null
}
reset = () => {
this.setState({
pickedImage: null
});
}
pickImageHandler = () => {
ImagePicker.showImagePicker({title: "Pick an Image",
maxWidth: 800, maxHeight: 600}, res => {
if (res.didCancel) {
console.log("User cancelled!");
} else if (res.error) {
console.log("Error", res.error);
} else {
this.setState({
pickedImage: { uri: res.uri }
});
}
});
}
resetHandler = () =>{
this.reset();
}
render() {
return (
<View style={styles.container}>
<Text style={styles.textStyle}>Pick Image From Camera and Gallery</Text>
<View style={styles.placeholder}>
<Image source={this.state.pickedImage} style={styles.previewImage} />
</View>
<View style={styles.button}>
<Button title="Pick Image" onPress={this.pickImageHandler} />
<Button title="Reset" onPress={this.resetHandler} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
alignItems:"center"
},
textStyle: {
fontWeight:"bold",
fontSize:30,
textAlign:"center",
color:"red",
marginTop:10
},
placeholder: {
borderWidth: 1,
borderColor: "black",
backgroundColor: "#eee",
width: "70%",
height: 280,
marginTop:50,
},
button: {
width: "80%",
marginTop:20,
flexDirection:"row",
justifyContent: "space-around"
},
previewImage: {
width: "100%",
height: "100%"
}
});
}
Source: react-native-image-picker

Related

How to open downloaded photo using gallery app in React Native Expo?

I am facing a problem, cannot figure out how to implement such action. I want to Download Photo from external source, and then I want to open it in gallery.
Checked this source: How to open iOS gallery app from React Native app
Here one developer suggest to use this code:
openPhotos = () =>{
switch(Platform.OS){
case "ios":
Linking.openURL("photos-redirect://");
break;
case "android":
Linking.openURL("content://media/internal/images/media");
break;
default:
console.log("Could not open gallery app");
}
}
This code does open gallery, but when I select default gallery app, it shows black screen, if I choose google photos app it opens the gallery without black screen.
My question would be how could I refactor my code, to be able to Download Photo, and open downloaded photo in gallery?
Component code:
import React from "react";
import {View,Text, StyleSheet,Platform,Image,Alert} from "react-native";
import PhotoComments from "./PhotoComments";
import moment from "moment";
import * as MediaLibrary from "expo-media-library";
import * as FileSystem from "expo-file-system";
import * as Permissions from "expo-permissions";
import { Button } from "react-native-elements";
import { Linking } from "expo";
function downloadFile(uri) {
let filename = uri.split("/");
filename = filename[filename.length - 1];
let fileUri = FileSystem.documentDirectory + filename;
FileSystem.downloadAsync(uri, fileUri)
.then(({ uri }) => {
saveFile(uri);
})
.catch(error => {
Alert.alert("Error", "Couldn't download photo");
console.error(error);
});
}
async function openPhotos(uri) {
switch (Platform.OS) {
case "ios":
Linking.openURL("photos-redirect://");
break;
case "android":
//Linking.openURL("content://media/internal/images/media/");
Linking.openURL("content://media/internal/images/media");
break;
default:
console.log("Could not open gallery app");
}
}
async function saveFile(fileUri) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri);
const data = await MediaLibrary.createAlbumAsync("Download", asset, false);
console.log("deubeuger");
console.log(data);
console.log("buger");
Alert.alert("Success!", JSON.stringify(fileUri));
openPhotos(fileUri);
}
}
const PhotoRecord = ({ data }) => {
return (
<View style={styles.container}>
<View style={styles.infoContainer}>
<Text style={styles.usernameLabel}>#{data.username}</Text>
<Text style={styles.addedAtLabel}>
{moment(new Date(data.addedAt)).format("YYYY/MM/DD HH:mm")}
</Text>
</View>
<View style={styles.imageContainer}>
<Image source={{ uri: data.links.thumb }} style={styles.image} />
</View>
<PhotoComments comments={data.photoComments} />
<View style={styles.btnContainer}>
<Button
buttonStyle={{
backgroundColor: "white",
borderWidth: 1
}}
titleStyle={{ color: "dodgerblue" }}
containerStyle={{ backgroundColor: "yellow" }}
title="Add Comment"
/>
<Button
onPress={() => downloadFile(data.links.image)}
style={styles.btn}
title="Download"
/>
</View>
</View>
);
};
I managed to implement downloading from external source, but cannot find the working solutions on how to open downloaded photo through gallery app.
Maybe I am looking for solution which is not efficient, maybe there is a better way?
Couldn't find desirable solution for this problem. Decided to develop an app a little bit differently, if someone with similar problem will search for this thread. I made Download Button which will Download photo to the device
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
Image,
Alert,
TouchableOpacity
} from "react-native";
import PhotoComments from "./PhotoComments";
import moment from "moment";
import * as MediaLibrary from "expo-media-library";
import * as FileSystem from "expo-file-system";
import * as Permissions from "expo-permissions";
import { Button } from "react-native-elements";
import ZoomableImage from "./ZoomableImage";
function downloadFile(uri) {
let filename = uri.split("/");
filename = filename[filename.length - 1];
let fileUri = FileSystem.documentDirectory + filename;
FileSystem.downloadAsync(uri, fileUri)
.then(({ uri }) => {
saveFile(uri);
})
.catch(error => {
Alert.alert("Error", "Couldn't download photo");
console.error(error);
});
}
async function saveFile(fileUri) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri);
await MediaLibrary.createAlbumAsync("Download", asset, false);
Alert.alert("Success", "Image was successfully downloaded!");
}
}
const PhotoRecord = ({ data }) => {
const [show, setShow] = useState(false);
return (
<View style={styles.container}>
<ZoomableImage
show={show}
setShow={setShow}
imageSource={data.links.image}
/>
<View style={styles.infoContainer}>
<Text style={styles.usernameLabel}>#{data.username}</Text>
<Text style={styles.addedAtLabel}>
{moment(new Date(data.addedAt)).format("YYYY/MM/DD HH:mm")}
</Text>
</View>
<TouchableOpacity
activeOpacity={1}
style={styles.imageContainer}
onLongPress={() => setShow(true)}
>
<Image source={{ uri: data.links.thumb }} style={styles.image} />
</TouchableOpacity>
<PhotoComments comments={data.photoComments} />
<View style={styles.btnContainer}>
<Button
buttonStyle={{
backgroundColor: "white",
borderWidth: 1
}}
titleStyle={{ color: "dodgerblue" }}
containerStyle={{ backgroundColor: "yellow" }}
title="Add Comment"
/>
<Button
onPress={() => downloadFile(data.links.image)}
style={styles.btn}
title="Download"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
display: "flex",
flexDirection: "column"
},
infoContainer: {
borderBottomWidth: 1,
borderColor: "gainsboro",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
padding: 15
},
usernameLabel: {
fontSize: 18,
fontWeight: "bold"
},
addedAtLabel: {
paddingTop: 10,
color: "#404040"
},
imageContainer: {
width: "100%",
height: 380
},
image: {
width: "100%",
height: "100%",
resizeMode: "cover"
},
btnContainer: {
flex: 1,
flexDirection: "row",
marginBottom: 100,
justifyContent: "space-between"
}
});
export default PhotoRecord;
On my device it looks like this
If Download button clicked it will download the photo to the device
If user want to inspect the image, he can do long press on the photo and then the photo will be open in a web view modal
This is far from perfect, but I could figure out by myself.
The code for modal is here:
import React from "react";
import { Modal, Dimensions, StyleSheet, View } from "react-native";
import { WebView } from "react-native-webview";
const ZoomableImage = ({ show, setShow, imageSource }) => {
return (
<Modal
animationType={"fade"}
transparent={false}
visible={show}
onRequestClose={() => {
setShow(!show);
}}
>
<View style={styles.container}>
<WebView source={{ uri: imageSource }} style={styles.image} />
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "space-between"
},
image: {
height: Math.round(Dimensions.get("window").height),
width: Math.round(Dimensions.get("window").width),
flex: 1
}
});
export default ZoomableImage;
Couldn't achieve what I wanted but came up with a slightly different solution, hopes this will help someone.

How to display toast message in react native

I am trying to display a toast message in react native on button click
import React,{ Component } from 'react';
import { StyleSheet,TextInput, View, Button, Text, ToastAndroid } from 'react-native';
export default class App extends Component {
state = {
placeName : "",
titleText: "Text view"
}
placeNameChangeHandler = val =>{
this.setState({
placeName : val
})
}
placeSubmitHandler = () =>{
this.setState({
titleText: this.state.placeName.trim()
})
}
render() {
return (
<View style={styles.rootContainer}>
<View style={styles.btnEditContainer}>
<View style ={styles.wrapStyle}>
<TextInput
style = {styles.textInputStyle}
value = {this.state.placeName}
onChangeText = {this.placeNameChangeHandler}
/>
</View>
<View style ={styles.wrapStyle}>
<Button
title="Add"
style ={styles.buttonStyle}
onPress ={this.placeSubmitHandler}/>
</View>
</View>
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
<Text
style ={styles.textStyle}>
{this.state.titleText}
</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
rootContainer: {
height:"100%",
width:"100%",
backgroundColor: "#008000",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
btnEditContainer: {
backgroundColor:"#008080",
flexDirection:"row",
alignItems:"center",
justifyContent: "center"
},
textContainer: {
backgroundColor:"#00FFFF",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
textStyle: {
fontSize: 20,
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
buttonStyle: {
},
textInputStyle: {
borderColor:"black",
borderWidth:1,
},
wrapStyle: { marginLeft:5,
marginRight:5 },
});
Can use below notifyMessage to show toast message:
import {
ToastAndroid,
Platform,
AlertIOS,
} from 'react-native';
function notifyMessage(msg: string) {
if (Platform.OS === 'android') {
ToastAndroid.show(msg, ToastAndroid.SHORT)
} else {
AlertIOS.alert(msg);
}
}
OR
Use react-native-simple-toast in both iOS & Android.
import Toast from 'react-native-simple-toast';
Toast.show('This is a toast.');
Toast.show('This is a long toast.', Toast.LONG);
React Native now has built-in API Alert which works both on IOS and Android.
https://reactnative.dev/docs/alert
If you're looking for a simple, JS only implementation of toast notifications for react native which works on both iOS and Android you can have a look at
https://github.com/jeanverster/react-native-styled-toast
Usage:
import { useToast } from 'react-native-styled-toast'
const { toast } = useToast()
return <Button onPress={() => toast({ message: 'Check me out!', ...config })} />
You can also use the package toast message.
Check react-native-toast-message

Is there a way to hide google client ID when working with a front-end google sign in authentication with expo?

I'm currently working on a mobile project which uses react-native and expo. I am using google sign-in for authentication and I would like to hide the google clientID. However, I can't hide it in the '.env' file because this sign-in method is only client side and not server side. I also can't get the sign-in to work on iOS devices, is it because I don't have the iOS clientID? How do you create clientID for iOS? The clientID for android does work. All help is greatly appreciated!
import React from "react"
import { StyleSheet, Text, View, Image, Button } from "react-native"
import Expo from "expo"
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
signedIn: false,
name: "",
photoUrl: ""
}
}
signIn = async () => {
try {
const result = await Expo.Google.logInAsync({
androidClientId:
"833456763323-ig9ndr0tbvb62jv4ddn6j8pos3a49m35.apps.googleusercontent.com",
//iosClientId: YOUR_CLIENT_ID_HERE, <-- if you use iOS
scopes: ["profile", "email"]
})
if (result.type === "success") {
this.setState({
signedIn: true,
name: result.user.name,
photoUrl: result.user.photoUrl
})
} else {
console.log("cancelled")
}
} catch (e) {
console.log("error", e)
}
}
render() {
return (
<View style={styles.container}>
{this.state.signedIn ? (
<LoggedInPage name={this.state.name} photoUrl={this.state.photoUrl} />
) : (
<LoginPage signIn={this.signIn} />
)}
</View>
)
}
}
const LoginPage = props => {
return (
<View>
<Text style={styles.header}>Sign In With Google</Text>
<Button title="Sign in with Google" onPress={() => props.signIn()} />
</View>
)
}
const LoggedInPage = props => {
return (
<View style={styles.container}>
<Text style={styles.header}>Welcome:{props.name}</Text>
<Image style={styles.image} source={{ uri: props.photoUrl }} />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
},
header: {
fontSize: 25
},
image: {
marginTop: 15,
width: 150,
height: 150,
borderColor: "rgba(0,0,0,0.2)",
borderWidth: 3,
borderRadius: 150
}
})
use react-native-dotenv to hide your api keys, urls, etc. BUT for your client id you need not hide it, better setup restrictions on your firebase.
NOTE: YOUR API IS VISIBLE HERE, HYPOCRISY XD XD

How to open the camera and taking the picture in react native?

I want to open the device camera from my app when user click on the button and when user click on back button it should react to my application from device camera. I am able to open camera and take photo by running react native project. But I want to do it how camera works in what's app. That is clicking on button -> opening camera -> send button .
I am an beginner in react native .I tried many ways but I am not getting how it can be done.
Can anybody assist me to do this.
My App.js code is,
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import Camera from 'react-native-camera';
class BadInstagramCloneApp extends Component {
render() {
return (
<View style={styles.container}>
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
</Camera>
</View>
);
}
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({metadata: options})
.then((data) => console.log(data))
.catch(err => console.error(err));
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center'
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});
AppRegistry.registerComponent('BadInstagramCloneApp', () => BadInstagramCloneApp);
You can use the state to show/hide the camera view/component.
Please check the following code:
...
class BadInstagramCloneApp extends Component {
constructor(props) {
super(props);
this.state = {
isCameraVisiable: false
}
}
showCameraView = () => {
this.setState({ isCameraVisible: true });
}
render() {
const { isCameraVisible } = this.state;
return (
<View style={styles.container}>
{!isCameraVisible &&<Button title="Show me Camera" onPress={this.showCameraView} />}
{isCameraVisible &&
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
</Camera>}
</View>
);
}
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({metadata: options})
.then((data) => {
console.log(data);
this.setState({ isCameraVisible: false });
}
.catch(err => console.error(err));
}
}
...
You can use https://github.com/ivpusic/react-native-image-crop-picker for this. This component helps you to take photo and also the photo if required. Follow the documentation correctly and here is the code for camera selection option
ImagePicker.openCamera({
cropping: true,
width: 500,
height: 500,
cropperCircleOverlay: true,
compressImageMaxWidth: 640,
compressImageMaxHeight: 480,
freeStyleCropEnabled: true,
}).then(image => {
this.setState({imageModalVisible: false})
})
.catch(e => {
console.log(e), this.setState({imageModalVisible: false})
});
Correction of best answer because of deprecation of Camera to RNCamera plus missing closing bracket ")" right before the .catch and like a spelling mistake with the declaration of state:
But basically there's 2 routes, whether you're using expo or react native. You gotta have Pods/Ruby/Cocoapods or manually link and all that if you're using traditional React Native, but just go with expo-camera if you got an expo set up and don't listen to this.
This is a React-Native with Pods/Ruby/CocoaPods solution, whereas going with expo-camera might be much faster and better if you're not set up like this.
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
Button,
TouchableOpacity
} from 'react-native';
import { RNCamera } from 'react-native-camera';
export default class Camera2 extends Component {
constructor(props) {
super(props);
this.state = {
isCameraVisible: false
}
}
showCameraView = () => {
this.setState({ isCameraVisible: true });
}
takePicture = async () => {
try {
const data = await this.camera.takePictureAsync();
console.log('Path to image: ' + data.uri);
} catch (err) {
// console.log('err: ', err);
}
};
render() {
const { isCameraVisible } = this.state;
return (
<View style={styles.container}>
{!isCameraVisible &&<Button title="Show me Camera" onPress={this.showCameraView} />}
{isCameraVisible &&
<RNCamera
ref={cam => {
this.camera = cam;
}}
style={styles.preview}
>
<View style={styles.captureContainer}>
<TouchableOpacity style={styles.capture} onPress={this.takePicture}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
<Text>Take Photo</Text>
</TouchableOpacity>
</View>
</RNCamera>}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center'
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});

Load a local image after loading a remote image failed

Is it possible to load a local image if the remote image failed?
For example, I have the following code:
<Image style={ styles.userImage }
source={ { uri: http://example.com/my_image.jpg } }
onError={(error) => ...}
/>
In case for example I don't have the rights to access http://example.com/my_image.jpg, I'll get an error in onError. Is there a way then to load a local image instead?
Use component' state. In your constructor set initial url:
this.state = { image: { uri: 'http://example.com/my_image.jpg' } }
Create onError handler:
onError(error){
this.setState({ image: require('your_local_image.path')})
}
And then combine it all together:
<Image style={ styles.userImage }
source={ this.state.image }
onError={ this.onError.bind(this) }
/>
As per the latest docs you can use defaultSource property. It shows the image till the original image loads, if the load fails the default image is shown Link to docs
To elaborate on Cherniv's answer you could create an <Images /> component that abstracts this away for you:
import React from 'react';
import { Image } from 'react-native';
export default class Images extends React.Component {
static defaultProps = {
source: [],
onError: () => {},
}
state = { current: 0 }
onError = error => {
this.props.onError(error);
const next = this.state.current + 1;
if (next < this.props.source.length) {
this.setState({ current: next });
}
}
render() {
const { onError, source, ...rest } = this.props;
return (
<Image
source={source[this.state.current]}
onError={this.onError}
{...rest}
/>
);
}
}
Then you can use it like this:
import Images from './Images';
<Images
source={[
{ uri: 'http://example.com/bad_image.jpg' },
{ uri: 'http://example.com/good_image.jpg' },
require('./default.jpg'),
]}
style={{
backgroundColor: '#ccc',
height: 200,
width: 200,
}}
/>
Create a component ImageLoad like this:
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import { Image, ImageBackground, ActivityIndicator, View } from 'react-native';
class ImageLoad extends PureComponent {
static propTypes = {
isShowActivity: PropTypes.bool,
};
static defaultProps = {
isShowActivity: true,
};
constructor(props) {
super(props);
this.state = {
isLoaded: false,
isError: false
};
}
onLoadEnd(){
this.setState({
isLoaded: true
});
}
onError(){
this.setState({
isError: true
});
}
render() {
const {
style, source, resizeMode, borderRadius, backgroundColor, children,
loadingStyle, placeholderSource, placeholderStyle,
customImagePlaceholderDefaultStyle
} = this.props;
return(
<ImageBackground
onLoadEnd={this.onLoadEnd.bind(this)}
onError={this.onError.bind(this)}
style={[styles.backgroundImage, style]}
source={source}
resizeMode={resizeMode}
borderRadius={borderRadius}
>
{
(this.state.isLoaded && !this.state.isError) ? children :
<View
style={[styles.viewImageStyles, { borderRadius: borderRadius }, backgroundColor ? { backgroundColor: backgroundColor } : {}]}
>
{
(this.props.isShowActivity && !this.state.isError) &&
<ActivityIndicator
style={styles.activityIndicator}
size={loadingStyle ? loadingStyle.size : 'small'}
color={loadingStyle ? loadingStyle.color : 'gray'}
/>
}
<Image
style={placeholderStyle ? placeholderStyle : [styles.imagePlaceholderStyles, customImagePlaceholderDefaultStyle]}
source={placeholderSource ? placeholderSource : require('./Images/empty-image.png')}
>
</Image>
</View>
}
{
this.props.children &&
<View style={styles.viewChildrenStyles}>
{
this.props.children
}
</View>
}
</ImageBackground>
);
}
}
const styles = {
backgroundImage: {
position: 'relative',
},
activityIndicator: {
position: 'absolute',
margin: 'auto',
zIndex: 9,
},
viewImageStyles: {
flex: 1,
backgroundColor: '#e9eef1',
justifyContent: 'center',
alignItems: 'center'
},
imagePlaceholderStyles: {
width: 100,
height: 100,
resizeMode: 'contain',
justifyContent: 'center',
alignItems: 'center'
},
viewChildrenStyles: {
top: 0,
left: 0,
right: 0,
bottom: 0,
position: 'absolute',
backgroundColor: 'transparent'
}
}
export default ImageLoad;
and use this anywhere in your app:
<ImageLoad
style={{ width: 320, height: 250 }}
loadingStyle={{ size: 'large', color: 'blue' }}
source={{ uri: 'url image' }}
/>