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,