Skip to content

Commit

Permalink
Expo camera (#13)
Browse files Browse the repository at this point in the history
* camera

* actual camera

* camera with a button on the bottom

* save to camera rool

* move camera to tab2

* edit the layout of the camera page

* add an intermideate step to allow user accept or retake the photo

* fix some styling problem

* manually fix some styling error

* run linter and fix some styling problems

* Ignore Linter on Metro Config

* change style

* changed camera and info buttons + repositioned

---------

Co-authored-by: Mana Vale <[email protected]>
Co-authored-by: Yuxuan Xue <[email protected]>
Co-authored-by: Daniel Cufino <[email protected]>
Co-authored-by: mmmyr <[email protected]>
  • Loading branch information
5 people authored Feb 3, 2024
1 parent 81c51ac commit e283866
Show file tree
Hide file tree
Showing 17 changed files with 480 additions and 181 deletions.
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

0 comments on commit e283866

Please sign in to comment.