diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js index b6200f48c507..a1a3ed946967 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js @@ -1,7 +1,9 @@ import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, View} from 'react-native'; +import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import {RESULTS} from 'react-native-permissions'; +import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withDelay, withSequence, withSpring, withTiming} from 'react-native-reanimated'; import {useCameraDevices} from 'react-native-vision-camera'; import Hand from '@assets/images/hand.svg'; import Shutter from '@assets/images/shutter.svg'; @@ -66,6 +68,41 @@ function IOURequestStepScan({ const {translate} = useLocalize(); + const focusIndicatorOpacity = useSharedValue(0); + const focusIndicatorScale = useSharedValue(2); + const focusIndicatorPosition = useSharedValue({x: 0, y: 0}); + + const cameraFocusIndicatorAnimatedStyle = useAnimatedStyle(() => ({ + opacity: focusIndicatorOpacity.value, + transform: [{translateX: focusIndicatorPosition.value.x}, {translateY: focusIndicatorPosition.value.y}, {scale: focusIndicatorScale.value}], + })); + + const focusCamera = (point) => { + if (!camera.current) { + return; + } + + camera.current.focus(point).catch((ex) => { + if (ex.message === '[unknown/unknown] Cancelled by another startFocusAndMetering()') { + return; + } + Log.warn('Error focusing camera', ex); + }); + }; + + const tapGesture = Gesture.Tap() + .enabled(device && device.supportsFocus) + .onStart((ev) => { + const point = {x: ev.x, y: ev.y}; + + focusIndicatorOpacity.value = withSequence(withTiming(0.8, {duration: 250}), withDelay(1000, withTiming(0, {duration: 250}))); + focusIndicatorScale.value = 2; + focusIndicatorScale.value = withSpring(1, {damping: 10, stiffness: 200}); + focusIndicatorPosition.value = point; + + runOnJS(focusCamera)(point); + }); + useEffect(() => { const refreshCameraPermissionStatus = () => { CameraPermission.getCameraPermissionStatus() @@ -256,16 +293,20 @@ function IOURequestStepScan({ )} {cameraPermissionStatus === RESULTS.GRANTED && device != null && ( - - - + + + + + + )} diff --git a/src/styles/index.ts b/src/styles/index.ts index fa8e4284e0d7..0d4b420c00e4 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -866,6 +866,18 @@ const styles = (theme: ThemeColors) => justifyItems: 'center', }, + cameraFocusIndicator: { + position: 'absolute', + left: -32, + top: -32, + width: 64, + height: 64, + borderRadius: 32, + borderWidth: 2, + borderColor: theme.white, + pointerEvents: 'none', + }, + permissionView: { paddingVertical: 108, paddingHorizontal: 61,