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 camera #13

Merged
merged 13 commits into from
Feb 3, 2024
38 changes: 24 additions & 14 deletions hmns-app/client/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,43 @@ import { StyleSheet } from "react-native";
import EditScreenInfo from "../../components/EditScreenInfo";
import { Text, View } from "../../components/Themed";

export default function TabOneScreen () {
export default function TabOneScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Tab One</Text>
<View
style={styles.separator}
lightColor='#eee'
darkColor='rgba(255,255,255,0.1)'
/>
<EditScreenInfo path='app/(tabs)/index.tsx' />
<View style={styles.header}>
<Text style={styles.title}>Tab One</Text>
<View
style={styles.separator}
lightColor='#eee'
darkColor='rgba(255,255,255,0.1)'
/>
<EditScreenInfo path='app/(tabs)/index.tsx' />
</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
alignItems: "center",
flex: 1,
justifyContent: "center"
},
header: {
alignItems: "center",
justifyContent: "center",
// Adjust the flex value or height as needed
flex: 0.45,
},
separator: {
height: 1,
marginVertical: 30,
width: "80%"
width: "80%",
},
title: {
fontSize: 20,
fontWeight: "bold"
}
fontWeight: "bold",
},
camera: {
flex: 1, // Take up all available space
},
});

22 changes: 4 additions & 18 deletions hmns-app/client/app/(tabs)/two.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import { StyleSheet } from "react-native";
import CameraComponent from "../../components/CameraComponent";
import { View } from "../../components/Themed";

import EditScreenInfo from "../../components/EditScreenInfo";
import { Text, View } from "../../components/Themed";

export default function TabTwoScreen () {
export default function TabTwoScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Tab Two</Text>
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
<EditScreenInfo path="app/(tabs)/two.tsx" />
<CameraComponent/>
</View>
);
}

const styles = StyleSheet.create({
container: {
alignItems: "center",
flex: 1,
justifyContent: "center"
},
separator: {
height: 1,
marginVertical: 30,
width: "80%"
},
title: {
fontSize: 20,
fontWeight: "bold"
}
});
Binary file added hmns-app/client/assets/images/gallery.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions hmns-app/client/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
// Required for expo-router
'expo-router/babel',
],
};
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [
// Required for expo-router
"expo-router/babel",
],
};
};
Binary file not shown.
264 changes: 264 additions & 0 deletions hmns-app/client/components/CameraComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
import React, { useState, useEffect, useRef } from "react";
import { View, Text, StyleSheet, TouchableOpacity, Image } from "react-native";
import { Camera } from "expo-camera";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { Pressable, useColorScheme } from "react-native";
import Colors from "../constants/Colors";
import * as MediaLibrary from "expo-media-library";
import { StatusBar } from "react-native";


export default function CameraComponent() {
const [hasPermission, setHasPermission] = useState(null);
// State to store the captured photo
const [photo, setPhoto] = useState(null);
// Reference to the camera component
const cameraRef = useRef(null);
// Function to show photo-taking tips
const showPhotoTips = () => {
alert("Photo-taking Tips:\n1. Keep your hands steady.\n2. Make sure there's enough light.\n3. Focus on the butterfly you want to search up before taking the photo.");
};
const [confirmVisible, setConfirmVisible] = useState(false);


// Request camera permissions when the component mounts
useEffect(() => {
(async () => {
// Request camera permission
const cameraStatus = await Camera.requestCameraPermissionsAsync();
setHasPermission(cameraStatus.status === "granted");


// Request media library permission
const mediaLibraryStatus = await MediaLibrary.requestPermissionsAsync();
if (mediaLibraryStatus.status !== "granted") {
alert("Sorry, we need camera roll permissions to save the image!");
}
})();
}, []);


// // Capture a photo and save it to photo album
// const takePhoto = async () => {
// if (cameraRef.current) {
// const options = { quality: 0.5, base64: true, skipProcessing: true };
// let newPhoto = await cameraRef.current.takePictureAsync(options);
// setPhoto(newPhoto);


// // Save the photo to the gallery
// const asset = await MediaLibrary.createAssetAsync(newPhoto.uri);
// await MediaLibrary.createAlbumAsync('HMNS', asset, false); // Replace 'YourAppName' with your app's name or any other desired album name.
// }
// };
const takePhoto = async () => {
if (cameraRef.current) {
const options = { quality: 0.5, base64: true, skipProcessing: true };
const newPhoto = await cameraRef.current.takePictureAsync(options);
setPhoto(newPhoto);
setConfirmVisible(true); // Show the confirmation screen
}
};


// Retake the photo
const retakePhoto = () => {
setPhoto(null);
setConfirmVisible(false); // Hide the confirmation screen
};


// Accept the photo and save it
const acceptPhoto = async () => {
if (photo) {
try {
// Save the photo to the gallery here
const asset = await MediaLibrary.createAssetAsync(photo.uri);
await MediaLibrary.createAlbumAsync("YourAppName", asset, false);

// Handle any operation after saving the photo, such as navigation or state reset
setConfirmVisible(false); // Hide confirmation screen
setPhoto(null); // Reset photo state
} catch (error) {
// Handle any errors here
console.error("Error saving photo", error);
alert("Error saving photo");
}
}
};


// If confirmation screen should be visible, show the taken photo and options
if (confirmVisible && photo) {
return (
<View style={styles.confirmContainer}>
<Image source={{ uri: photo.uri }} style={styles.preview} />
<View style={styles.confirmButtons}>
<TouchableOpacity onPress={retakePhoto} style={styles.confirmButton}>
<Text style={styles.confirmButtonText}>Retake</Text>
</TouchableOpacity>
<TouchableOpacity onPress={acceptPhoto} style={styles.confirmButton}>
<Text style={styles.confirmButtonText}>Accept</Text>
</TouchableOpacity>
</View>
</View>
);
}






// If permissions are still being requested, return an empty view
if (hasPermission === null) {
return <View />;
}
// If camera access is denied, inform the user
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}


// Make sure to hide the status bar to provide a full-screen experience
StatusBar.setHidden(true);


return (
<View style={styles.container}>
<Camera style={styles.camera} ref={cameraRef}>
{/* Overlay the UI components on the camera preview */}
<View style={styles.uiContainer}>
<View style={styles.topToolbar}>
{/* Info Button */}
<TouchableOpacity style={styles.tipsButton} onPress={showPhotoTips}>
<FontAwesome
name='info-circle'
size={30}
color="white"
style={{ marginLeft: 15 }}
/>
</TouchableOpacity>
</View>

<View style={styles.bottomToolbar}>
{/* Photo Button */}
<TouchableOpacity style={styles.captureButton} onPress={takePhoto}>
<View style={styles.captureButtonRing} />
</TouchableOpacity>
</View>


</View>
</Camera>
</View>
);
}


const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "black",
},
camera: {
flex: 1,
justifyContent: "space-between",
},
uiContainer: {
flex: 1,
backgroundColor: "transparent",
flexDirection: "column",
justifyContent: "space-between",
},
topToolbar: {
flex: 0.1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-start",
paddingTop: 20,
paddingHorizontal: 20,
},
bottomToolbar: {
padding: 20,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)"
},
captureButton: {
width: 70,
height: 70,
borderWidth: 4,
borderColor: "white",
borderRadius: 35,
backgroundColor: "white",
alignSelf: "center",
justifyContent: "center",
alignItems:"center",
},
captureButtonRing: {
width: 65,
height: 65,
borderWidth: 2,
borderColor: "black",
borderRadius: 32.5,
position: "absolute",
},
galleryButton: {
width: 50,
height: 50,
borderRadius: 10,
alignSelf: "center",
overflow: "hidden",
backgroundColor: "white",
},
thumbnail: {
width: 45,
height: 45,
backgroundColor: "transparent",
},
tipsButton: {
// Style your button as needed
position: 'absolute',
borderRadius: 10,
padding: 10,
top: 20,
left: 10,
},
confirmContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
confirmButtons: {
flexDirection: "row",
//justifyContent: "space-around",
width: "100%",
padding: 20,
},
confirmButton: {
flex: 1,
alignItems: "center",
justifyContent: "center",
marginHorizontal: 10,
paddingVertical: 10,
borderRadius: 5,
backgroundColor: "rgba(0,0,0,0.5)",
},
preview: {
width: "100%", // You may need to adjust this
height: "80%", // You may need to adjust this
borderRadius: 4,
},
confirmButtonText: {
// Style for the text inside your confirm buttons
color: "white",
fontSize: 18,
textAlign: "center",
},
});




Loading
Loading