feat: Add "Camera Devices" screen to Example (#1927)
* feat: Add "Camera Devices" screen to Example * feat: Store device in MMKV
This commit is contained in:
parent
658695dca5
commit
1843f7ac3a
@ -27,6 +27,9 @@ PODS:
|
|||||||
- libwebp/sharpyuv (1.3.2)
|
- libwebp/sharpyuv (1.3.2)
|
||||||
- libwebp/webp (1.3.2):
|
- libwebp/webp (1.3.2):
|
||||||
- libwebp/sharpyuv
|
- libwebp/sharpyuv
|
||||||
|
- MMKV (1.3.1):
|
||||||
|
- MMKVCore (~> 1.3.1)
|
||||||
|
- MMKVCore (1.3.1)
|
||||||
- RCT-Folly (2021.07.22.00):
|
- RCT-Folly (2021.07.22.00):
|
||||||
- boost
|
- boost
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
@ -331,6 +334,9 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- react-native-cameraroll (5.7.2):
|
- react-native-cameraroll (5.7.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- react-native-mmkv (2.10.2):
|
||||||
|
- MMKV (>= 1.2.13)
|
||||||
|
- React-Core
|
||||||
- react-native-safe-area-context (4.7.1):
|
- react-native-safe-area-context (4.7.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-video (5.2.1):
|
- react-native-video (5.2.1):
|
||||||
@ -338,7 +344,7 @@ PODS:
|
|||||||
- react-native-video/Video (= 5.2.1)
|
- react-native-video/Video (= 5.2.1)
|
||||||
- react-native-video/Video (5.2.1):
|
- react-native-video/Video (5.2.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-worklets-core (0.2.0):
|
- react-native-worklets-core (0.2.1):
|
||||||
- React
|
- React
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
@ -458,7 +464,7 @@ PODS:
|
|||||||
- SDWebImageWebPCoder (~> 0.8.4)
|
- SDWebImageWebPCoder (~> 0.8.4)
|
||||||
- RNGestureHandler (2.12.1):
|
- RNGestureHandler (2.12.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNReanimated (3.4.2):
|
- RNReanimated (3.5.4):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- FBLazyVector
|
- FBLazyVector
|
||||||
- glog
|
- glog
|
||||||
@ -534,6 +540,7 @@ DEPENDENCIES:
|
|||||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||||
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
|
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
|
||||||
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
|
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
|
||||||
|
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
|
||||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||||
- react-native-video (from `../node_modules/react-native-video`)
|
- react-native-video (from `../node_modules/react-native-video`)
|
||||||
- react-native-worklets-core (from `../node_modules/react-native-worklets-core`)
|
- react-native-worklets-core (from `../node_modules/react-native-worklets-core`)
|
||||||
@ -568,6 +575,8 @@ SPEC REPOS:
|
|||||||
- fmt
|
- fmt
|
||||||
- libevent
|
- libevent
|
||||||
- libwebp
|
- libwebp
|
||||||
|
- MMKV
|
||||||
|
- MMKVCore
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SDWebImageWebPCoder
|
- SDWebImageWebPCoder
|
||||||
- SocketRocket
|
- SocketRocket
|
||||||
@ -620,6 +629,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/@react-native-community/blur"
|
:path: "../node_modules/@react-native-community/blur"
|
||||||
react-native-cameraroll:
|
react-native-cameraroll:
|
||||||
:path: "../node_modules/@react-native-camera-roll/camera-roll"
|
:path: "../node_modules/@react-native-camera-roll/camera-roll"
|
||||||
|
react-native-mmkv:
|
||||||
|
:path: "../node_modules/react-native-mmkv"
|
||||||
react-native-safe-area-context:
|
react-native-safe-area-context:
|
||||||
:path: "../node_modules/react-native-safe-area-context"
|
:path: "../node_modules/react-native-safe-area-context"
|
||||||
react-native-video:
|
react-native-video:
|
||||||
@ -687,6 +698,8 @@ SPEC CHECKSUMS:
|
|||||||
hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
|
hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||||
|
MMKV: 5a07930c70c70b86cd87761a42c8f3836fb681d7
|
||||||
|
MMKVCore: e50135dbd33235b6ab390635991bab437ab873c0
|
||||||
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||||
RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
|
RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
|
||||||
RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3
|
RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3
|
||||||
@ -704,9 +717,10 @@ SPEC CHECKSUMS:
|
|||||||
React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95
|
React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95
|
||||||
react-native-blur: cfdad7b3c01d725ab62a8a729f42ea463998afa2
|
react-native-blur: cfdad7b3c01d725ab62a8a729f42ea463998afa2
|
||||||
react-native-cameraroll: 134805127580aed23403b8c2cb1548920dd77b3a
|
react-native-cameraroll: 134805127580aed23403b8c2cb1548920dd77b3a
|
||||||
|
react-native-mmkv: 9ae7ca3977e8ef48dbf7f066974eb844c20b5fd7
|
||||||
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
|
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
|
||||||
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
|
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
|
||||||
react-native-worklets-core: 7ad416a8965086b98b07964f7f6932560a54a14c
|
react-native-worklets-core: 7de763135ed696ba16e8d5471e41f595ba9802bb
|
||||||
React-NativeModulesApple: c57f3efe0df288a6532b726ad2d0322a9bf38472
|
React-NativeModulesApple: c57f3efe0df288a6532b726ad2d0322a9bf38472
|
||||||
React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5
|
React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5
|
||||||
React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17
|
React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17
|
||||||
@ -726,7 +740,7 @@ SPEC CHECKSUMS:
|
|||||||
ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9
|
ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9
|
||||||
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
||||||
RNGestureHandler: c0d04458598fcb26052494ae23dda8f8f5162b13
|
RNGestureHandler: c0d04458598fcb26052494ae23dda8f8f5162b13
|
||||||
RNReanimated: 726395a2fa2f04cea340274ba57a4e659bc0d9c1
|
RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
|
||||||
RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7
|
RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7
|
||||||
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
||||||
RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9
|
RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
"react-native": "^0.72.3",
|
"react-native": "^0.72.3",
|
||||||
"react-native-fast-image": "^8.6.3",
|
"react-native-fast-image": "^8.6.3",
|
||||||
"react-native-gesture-handler": "^2.12.1",
|
"react-native-gesture-handler": "^2.12.1",
|
||||||
|
"react-native-mmkv": "^2.10.2",
|
||||||
"react-native-pressable-opacity": "^1.0.10",
|
"react-native-pressable-opacity": "^1.0.10",
|
||||||
"react-native-reanimated": "^3.4.2",
|
"react-native-reanimated": "^3.5.4",
|
||||||
"react-native-safe-area-context": "^4.7.1",
|
"react-native-safe-area-context": "^4.7.1",
|
||||||
"react-native-screens": "^3.24.0",
|
"react-native-screens": "^3.24.0",
|
||||||
"react-native-static-safe-area-insets": "^2.2.0",
|
"react-native-static-safe-area-insets": "^2.2.0",
|
||||||
|
@ -8,6 +8,7 @@ import type { Routes } from './Routes'
|
|||||||
import { Camera, CameraPermissionStatus } from 'react-native-vision-camera'
|
import { Camera, CameraPermissionStatus } from 'react-native-vision-camera'
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||||
import { StyleSheet } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
|
import { DevicesPage } from './DevicesPage'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Routes>()
|
const Stack = createNativeStackNavigator<Routes>()
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ export function App(): React.ReactElement | null {
|
|||||||
presentation: 'transparentModal',
|
presentation: 'transparentModal',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen name="Devices" component={DevicesPage} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
@ -17,6 +17,7 @@ import type { Routes } from './Routes'
|
|||||||
import type { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import type { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { useIsFocused } from '@react-navigation/core'
|
import { useIsFocused } from '@react-navigation/core'
|
||||||
import { examplePlugin } from './frame-processors/ExamplePlugin'
|
import { examplePlugin } from './frame-processors/ExamplePlugin'
|
||||||
|
import { usePreferredCameraDevice } from './hooks/usePreferredCameraDevice'
|
||||||
|
|
||||||
const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera)
|
const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera)
|
||||||
Reanimated.addWhitelistedNativeProps({
|
Reanimated.addWhitelistedNativeProps({
|
||||||
@ -44,7 +45,8 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
|
|||||||
const [flash, setFlash] = useState<'off' | 'on'>('off')
|
const [flash, setFlash] = useState<'off' | 'on'>('off')
|
||||||
const [enableNightMode, setEnableNightMode] = useState(false)
|
const [enableNightMode, setEnableNightMode] = useState(false)
|
||||||
|
|
||||||
// camera format settings
|
// camera device settings
|
||||||
|
const [preferredDevice] = usePreferredCameraDevice()
|
||||||
const device = useCameraDevice(cameraPosition)
|
const device = useCameraDevice(cameraPosition)
|
||||||
|
|
||||||
const [targetFps, setTargetFps] = useState(60)
|
const [targetFps, setTargetFps] = useState(60)
|
||||||
@ -170,7 +172,7 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
|
|||||||
<ReanimatedCamera
|
<ReanimatedCamera
|
||||||
ref={camera}
|
ref={camera}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
device={device}
|
device={preferredDevice ?? device}
|
||||||
format={format}
|
format={format}
|
||||||
fps={fps}
|
fps={fps}
|
||||||
hdr={enableHdr}
|
hdr={enableHdr}
|
||||||
@ -230,6 +232,9 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
|
|||||||
<IonIcon name={enableNightMode ? 'moon' : 'moon-outline'} color="white" size={24} />
|
<IonIcon name={enableNightMode ? 'moon' : 'moon-outline'} color="white" size={24} />
|
||||||
</PressableOpacity>
|
</PressableOpacity>
|
||||||
)}
|
)}
|
||||||
|
<PressableOpacity style={styles.button} onPress={() => navigation.navigate('Devices')}>
|
||||||
|
<IonIcon name="settings-outline" color="white" size={24} />
|
||||||
|
</PressableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
217
package/example/src/DevicesPage.tsx
Normal file
217
package/example/src/DevicesPage.tsx
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import type { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
|
import React, { useCallback, useMemo } from 'react'
|
||||||
|
import IonIcon from 'react-native-vector-icons/Ionicons'
|
||||||
|
import { StyleSheet, View, Text, ListRenderItemInfo, SectionList, SectionListData } from 'react-native'
|
||||||
|
import { CameraDevice, useCameraDevices } from 'react-native-vision-camera'
|
||||||
|
import { CONTENT_SPACING, SAFE_AREA_PADDING } from './Constants'
|
||||||
|
import type { Routes } from './Routes'
|
||||||
|
import { PressableOpacity } from 'react-native-pressable-opacity'
|
||||||
|
import { usePreferredCameraDevice } from './hooks/usePreferredCameraDevice'
|
||||||
|
|
||||||
|
const keyExtractor = (item: CameraDevice): string => item.id
|
||||||
|
|
||||||
|
interface SectionType {
|
||||||
|
position: CameraDevice['position'] | 'preferred'
|
||||||
|
}
|
||||||
|
type SectionData = SectionListData<CameraDevice, SectionType>
|
||||||
|
|
||||||
|
interface DeviceProps {
|
||||||
|
device: CameraDevice
|
||||||
|
onPress: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function Device({ device, onPress }: DeviceProps): React.ReactElement {
|
||||||
|
const maxPhotoRes = useMemo(
|
||||||
|
() =>
|
||||||
|
device.formats.reduce((prev, curr) => {
|
||||||
|
if (curr.photoWidth * curr.photoHeight > prev.photoWidth * prev.photoHeight) return curr
|
||||||
|
return prev
|
||||||
|
}),
|
||||||
|
[device.formats],
|
||||||
|
)
|
||||||
|
const maxVideoRes = useMemo(
|
||||||
|
() =>
|
||||||
|
device.formats.reduce((prev, curr) => {
|
||||||
|
if (curr.videoWidth * curr.videoHeight > prev.videoWidth * prev.videoHeight) return curr
|
||||||
|
return prev
|
||||||
|
}),
|
||||||
|
[device.formats],
|
||||||
|
)
|
||||||
|
const deviceTypes = useMemo(() => device.physicalDevices.map((t) => t.replace('-camera', '')).join(' + '), [device.physicalDevices])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PressableOpacity style={styles.itemContainer} onPress={onPress}>
|
||||||
|
<View style={styles.horizontal}>
|
||||||
|
<IonIcon name="camera" size={18} color="black" style={styles.icon} />
|
||||||
|
<Text style={styles.deviceName} numberOfLines={3}>
|
||||||
|
{device.name} <Text style={styles.devicePosition}>({device.position})</Text>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.deviceTypes}>{deviceTypes}</Text>
|
||||||
|
<View style={styles.horizontal}>
|
||||||
|
<IonIcon name="camera" size={12} color="black" style={styles.inlineIcon} />
|
||||||
|
<Text style={styles.resolutionText}>
|
||||||
|
{maxPhotoRes.photoWidth}x{maxPhotoRes.photoHeight}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.horizontal}>
|
||||||
|
<IonIcon name="videocam" size={12} color="black" style={styles.inlineIcon} />
|
||||||
|
<Text style={styles.resolutionText}>
|
||||||
|
{maxVideoRes.videoWidth}x{maxVideoRes.videoHeight} @ {maxVideoRes.maxFps} FPS
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.deviceId} numberOfLines={2} ellipsizeMode="middle">
|
||||||
|
{device.id}
|
||||||
|
</Text>
|
||||||
|
</PressableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = NativeStackScreenProps<Routes, 'Devices'>
|
||||||
|
export function DevicesPage({ navigation }: Props): React.ReactElement {
|
||||||
|
const devices = useCameraDevices()
|
||||||
|
const [preferredDevice, setPreferredDevice] = usePreferredCameraDevice()
|
||||||
|
|
||||||
|
const sections = useMemo((): SectionData[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
position: 'preferred',
|
||||||
|
data: preferredDevice != null ? [preferredDevice] : [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: 'back',
|
||||||
|
data: devices.filter((d) => d.position === 'back'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: 'front',
|
||||||
|
data: devices.filter((d) => d.position === 'front'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: 'external',
|
||||||
|
data: devices.filter((d) => d.position === 'external'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, [devices, preferredDevice])
|
||||||
|
|
||||||
|
const onDevicePressed = useCallback(
|
||||||
|
(device: CameraDevice) => {
|
||||||
|
setPreferredDevice(device)
|
||||||
|
navigation.navigate('CameraPage')
|
||||||
|
},
|
||||||
|
[navigation, setPreferredDevice],
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderItem = useCallback(
|
||||||
|
({ item }: ListRenderItemInfo<CameraDevice>) => {
|
||||||
|
return <Device device={item} onPress={() => onDevicePressed(item)} />
|
||||||
|
},
|
||||||
|
[onDevicePressed],
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderSectionHeader = useCallback(({ section }: { section: SectionData }) => {
|
||||||
|
if (section.data.length === 0) return null
|
||||||
|
return (
|
||||||
|
<View style={styles.sectionHeader}>
|
||||||
|
<Text style={styles.sectionHeaderText}>{section.position.toUpperCase()}</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.headerContainer}>
|
||||||
|
<View style={styles.horizontal}>
|
||||||
|
<PressableOpacity style={styles.backButton} onPress={navigation.goBack}>
|
||||||
|
<IonIcon name="chevron-back" size={35} color="black" style={styles.icon} />
|
||||||
|
</PressableOpacity>
|
||||||
|
<Text style={styles.header}>Camera Devices</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.subHeader}>
|
||||||
|
These are all detected Camera devices on your phone. This list will automatically update as you plug devices in or out.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<SectionList
|
||||||
|
style={styles.list}
|
||||||
|
contentContainerStyle={styles.listContent}
|
||||||
|
sections={sections}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
|
renderItem={renderItem}
|
||||||
|
renderSectionHeader={renderSectionHeader}
|
||||||
|
stickySectionHeadersEnabled={false}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
},
|
||||||
|
headerContainer: {
|
||||||
|
paddingTop: SAFE_AREA_PADDING.paddingTop,
|
||||||
|
paddingLeft: SAFE_AREA_PADDING.paddingLeft,
|
||||||
|
paddingRight: SAFE_AREA_PADDING.paddingRight,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
fontSize: 38,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
maxWidth: '80%',
|
||||||
|
},
|
||||||
|
subHeader: {
|
||||||
|
marginTop: 10,
|
||||||
|
fontSize: 18,
|
||||||
|
maxWidth: '80%',
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
marginTop: CONTENT_SPACING,
|
||||||
|
},
|
||||||
|
listContent: {
|
||||||
|
paddingBottom: SAFE_AREA_PADDING.paddingBottom,
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
paddingHorizontal: CONTENT_SPACING / 2,
|
||||||
|
paddingVertical: 5,
|
||||||
|
},
|
||||||
|
sectionHeaderText: {
|
||||||
|
opacity: 0.4,
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
itemContainer: {
|
||||||
|
paddingHorizontal: CONTENT_SPACING,
|
||||||
|
paddingVertical: 7,
|
||||||
|
},
|
||||||
|
deviceName: {
|
||||||
|
fontSize: 17,
|
||||||
|
marginLeft: 5,
|
||||||
|
flexShrink: 1,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
devicePosition: {
|
||||||
|
opacity: 0.4,
|
||||||
|
},
|
||||||
|
deviceId: {
|
||||||
|
fontSize: 12,
|
||||||
|
opacity: 0.4,
|
||||||
|
},
|
||||||
|
deviceTypes: {
|
||||||
|
fontSize: 12,
|
||||||
|
opacity: 0.4,
|
||||||
|
},
|
||||||
|
horizontal: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
marginTop: 7,
|
||||||
|
},
|
||||||
|
icon: {},
|
||||||
|
inlineIcon: {},
|
||||||
|
resolutionText: {
|
||||||
|
marginLeft: 5,
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
})
|
@ -5,4 +5,5 @@ export type Routes = {
|
|||||||
path: string
|
path: string
|
||||||
type: 'video' | 'photo'
|
type: 'video' | 'photo'
|
||||||
}
|
}
|
||||||
|
Devices: undefined
|
||||||
}
|
}
|
||||||
|
20
package/example/src/hooks/usePreferredCameraDevice.ts
Normal file
20
package/example/src/hooks/usePreferredCameraDevice.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { useMMKVString } from 'react-native-mmkv'
|
||||||
|
import { CameraDevice } from '../../../src/CameraDevice'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
import { useCameraDevices } from '../../../src/hooks/useCameraDevices'
|
||||||
|
|
||||||
|
export function usePreferredCameraDevice(): [CameraDevice | undefined, (device: CameraDevice) => void] {
|
||||||
|
const [preferredDeviceId, setPreferredDeviceId] = useMMKVString('camera.preferredDeviceId')
|
||||||
|
|
||||||
|
const set = useCallback(
|
||||||
|
(device: CameraDevice) => {
|
||||||
|
setPreferredDeviceId(device.id)
|
||||||
|
},
|
||||||
|
[setPreferredDeviceId],
|
||||||
|
)
|
||||||
|
|
||||||
|
const devices = useCameraDevices()
|
||||||
|
const device = useMemo(() => devices.find((d) => d.id === preferredDeviceId), [devices, preferredDeviceId])
|
||||||
|
|
||||||
|
return [device, set]
|
||||||
|
}
|
@ -5746,15 +5746,20 @@ react-native-gesture-handler@^2.12.1:
|
|||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
|
react-native-mmkv@^2.10.2:
|
||||||
|
version "2.10.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.10.2.tgz#73f06bb710388f67bade031e7b8e42a6d2358e40"
|
||||||
|
integrity sha512-hNrZzwvIFyogJkqf//rVSw7EwceYqkx/jl3hb5tzct6qqwEmS1L9ybvnDjzDkaMyDeouQIqAnsdnb6AuDSrgQQ==
|
||||||
|
|
||||||
react-native-pressable-opacity@^1.0.10:
|
react-native-pressable-opacity@^1.0.10:
|
||||||
version "1.0.10"
|
version "1.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d"
|
resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d"
|
||||||
integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w==
|
integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w==
|
||||||
|
|
||||||
react-native-reanimated@^3.4.2:
|
react-native-reanimated@^3.5.4:
|
||||||
version "3.4.2"
|
version "3.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.4.2.tgz#744154fead6d8d31d5bd9ac617d8c84d74a6f697"
|
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz#a6c2b0c43b6dad246f5d276213974afedb8e3fc7"
|
||||||
integrity sha512-FbtG+f1PB005vDTJSv4zAnTK7nNXi+FjFgbAM5gOzIZDajfph2BFMSUstzIsN8T77+OKuugUBmcTqLnQ24EBVg==
|
integrity sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/plugin-transform-object-assign" "^7.16.7"
|
"@babel/plugin-transform-object-assign" "^7.16.7"
|
||||||
"@babel/preset-typescript" "^7.16.7"
|
"@babel/preset-typescript" "^7.16.7"
|
||||||
|
Loading…
Reference in New Issue
Block a user