mrousavy / react-native-vision-camera

📸 A powerful, high-performance React Native Camera library.
https://react-native-vision-camera.com
MIT License
7.55k stars 1.1k forks source link

💭 How to follow QR position #2736

Closed shacharcohen1997 closed 6 months ago

shacharcohen1997 commented 7 months ago

Question

I am really struggling with a specific task.. i am trying to follow a qr code with a square I tried the code below which almost works in 1 device but not in others and not in iphone

What I tried

import { StatusBar } from 'expo-status-bar';
import { useEffect, useRef, useState } from 'react';
import { Dimensions, PixelRatio, Platform, StyleSheet, View } from 'react-native';
import { Polygon, Svg } from 'react-native-svg';
import {
  Camera,
  Templates,
  useCameraDevice,
  useCameraFormat,
  useCameraPermission,
  useCodeScanner,
} from 'react-native-vision-camera';

// Import transpose functions and hook from your provided code
import { transposeFromStream, rotateRectangleToVertical, Rectangle } from './useTransposeFromStream'; // Adjust the path as needed

export default function App() {
  const pxDiff = Math.max(PixelRatio.get() - 1, 1);
  const { width: viewWidth, height: viewHeight } = Dimensions.get('window');
  const [targetFps] = useState(30);
  const [polygonPoints, setPolygonPoints] = useState('');

  // Get camera device and format
  const device = useCameraDevice('back');
  const format = useCameraFormat(device, Templates.Photo);
  const camera = useRef<Camera>(null);

  // Camera permission
  const { hasPermission, requestPermission } = useCameraPermission();

  const codeScanner = useCodeScanner({
    codeTypes: ['qr'],
    onCodeScanned: (codes,frame) => {
      if (codes.length === 0) return;
      const qrCode = codes[0];
      if (!qrCode.value || !qrCode.frame) return;
      // Use transpose functions to adjust the frame coordinates
      let bounds: Rectangle = { y: 0, x: 0, width: 0, height: 0 };
      bounds = transposeFromStream(qrCode.frame,frame, { width: viewWidth, height: viewHeight });

      // Rotate rectangle if necessary
      if (Platform.OS === 'ios') {
        bounds = rotateRectangleToVertical(bounds, { width: viewWidth, height: viewHeight });
      }

      const points = [
        { x: bounds.x, y: bounds.y }, // y-x
        { x: bounds.x + bounds.width, y: bounds.y }, // y-right
        { x: bounds.x + bounds.width, y: bounds.y + bounds.height }, // bottom-right
        { x: bounds.x, y: bounds.y + bounds.height }, // bottom-left
      ];
      setPolygonPoints(points.map((p) => `${p.x},${p.y}`).join(' '));
    },
  });

  useEffect(() => {
    !hasPermission && requestPermission();
  }, [hasPermission, requestPermission]);

  useEffect(() => {
    // Re-render the Svg component when the polygonPoints state changes
    // This will update the overlay with the QR code position and area
  }, [polygonPoints]);

  if (!device || !hasPermission) return null;

  return (
    <View style={styles.container}>
      <Camera
        ref={camera}
        fps={targetFps}
        isActive={true}
        pixelFormat="yuv"
        device={device}
        format={format}
        torch={'off'}
        codeScanner={codeScanner}
        enableZoomGesture={true}
        resizeMode="cover"
        style={StyleSheet.absoluteFill}
        onError={console.error}
      />
      <Svg height="100%" width="100%" viewBox={`0 0 ${viewWidth} ${viewHeight}`}>
        <Polygon points={polygonPoints} fill="none" stroke="red" strokeWidth="2" />
      </Svg>
      <StatusBar style="light" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

VisionCamera Version

3.9.2

Additional information

shacharcohen1997 commented 7 months ago

https://github.com/mrousavy/react-native-vision-camera/issues/2436#issuecomment-1925577421 made it perfect but i dont know how she made it

shacharcohen1997 commented 7 months ago

i managed to improve the code by using

// useTransposeFromStream.ts 
/** Determines the offset & scale values to translate stream coordinates to a layouts coordinate */
const getTransposeProperties = (source: Dimension, target: Dimension) => {
  const scaleRatio = Math.max(
    target.width / source.width,
    target.height / source.height,
  );
  // Represents the width and height of the stream, scaled down to fit the screen
  // This is without taking into account the cut off part of the stream (to fit the screen)
  const uncutDimensions = {
    width: source.width * scaleRatio,
    height: source.height * scaleRatio,
  };
  // Reprensents how much of the stream is cut off to fit the screen
  const cutDimensions = {
    width: (uncutDimensions.width - target.width)*(scaleRatio),
    height: (uncutDimensions.height - target.height)*scaleRatio,
  };
  const x = {
    offset: cutDimensions.width/ 2 ,
    scale: scaleRatio,
  };
  const y = {
    offset: cutDimensions.height / 2,
    scale: scaleRatio,
  };
//   console.log("scaleRatio", scaleRatio, "uncutDimensions", uncutDimensions, "cutDimensions", cutDimensions, "x", x, "y", y);

  return { x, y };
};

export const transposeFromStream = (
    { x, y, width, height }: Rectangle,
    source: Dimension, // replace 'any' with the actual type of 'stream'
    target: Dimension
  ) => {
    const transposeFromStreamProperties = getTransposeProperties(source, target);

    return {
      x:
        x * transposeFromStreamProperties.x.scale -
        transposeFromStreamProperties.x.offset,
      y:
        y * transposeFromStreamProperties.y.scale -
        transposeFromStreamProperties.y.offset,
      width: width * transposeFromStreamProperties.x.scale,
      height: height * transposeFromStreamProperties.y.scale,
    };
  };

/** Given a set of coordinates in a landscape picture, returns the corresponding coordinates for a vertical plan  */
export const rotateCoordinatesToVertical = (
  { x, y }: Point,
  { height }: Dimension,
) => {
  return {
    x: height - y,
    y: x,
  };
};

export const rotateRectangleToVertical = (
  rectangle: Rectangle,
  frame: Dimension,
) => {
  // Rotating by 90° means the width and height are swapped
  const width = rectangle.height;
  const height = rectangle.width;
  // Rotate the top left corner coordinates
  const rotatedCoordinates = rotateCoordinatesToVertical(rectangle, frame);
  // The top left corner coordinates now represent the top right corner of the rectangle in the new plane
  // So recalculate the new top left corner coordinates
  return {
    x: rotatedCoordinates.x - width,
    y: rotatedCoordinates.y,
    width,
    height,
  };
};

export type Dimension = {
  width: number;
  height: number;
};
export type Point = {
  x: number;
  y: number;
};
export type Rectangle = Point & Dimension;
shacharcohen1997 commented 7 months ago

https://github.com/mrousavy/react-native-vision-camera/assets/160487610/4a77146b-079e-4927-b727-eddc3a6d148c

tuannguyen0410 commented 6 months ago

Hi @shacharcohen1997 I have follow your idea and have a same problem. and I have try like this .maybe this will have help you. Viewbox of my camera is '0 0 414 709' you can get is by using View Ref

image

And my useCodeScanner function is like this.

image

By the way I just make a test in Rect . you can try it in Polygon . Many thank for your idea

tuannguyen0410 commented 6 months ago

Here is my result for this detection IMG_0008

tuannguyen0410 commented 6 months ago

@shacharcohen1997 I have try to draw code Scanner Border only when detected code

const topLeftX = codesFrame.x; const topLeftY = codesFrame.y; const topRightX = codesFrame.x + codesFrame.width; const topRightY = codesFrame.y; const bottomRightX = codesFrame.x + codesFrame.width; const bottomRightY = codesFrame.y + codesFrame.height; const bottomLeftX = codesFrame.x; const bottomLeftY = codesFrame.y + codesFrame.height; const paths = [ { d: M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3} }, { d: M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3} }, { d: M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3} }, { d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftY

divide for 3 just my calculate the stroke width: The Result is:

image
mrousavy commented 6 months ago

Hey I think this can be discussed on the community discordi nstead of GitHub issues.

shachar1997 commented 6 months ago

@shacharcohen1997 I have try to draw code Scanner Border only when detected code

const topLeftX = codesFrame.x; const topLeftY = codesFrame.y; const topRightX = codesFrame.x + codesFrame.width; const topRightY = codesFrame.y; const bottomRightX = codesFrame.x + codesFrame.width; const bottomRightY = codesFrame.y + codesFrame.height; const bottomLeftX = codesFrame.x; const bottomLeftY = codesFrame.y + codesFrame.height; const paths = [ { d: M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3} }, { d: M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3} }, { d: M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3} }, { d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftY

divide for 3 just my calculate the stroke width: The Result is:

image

can you send the full snippet or your code?

tuannguyen0410 commented 6 months ago

@shacharcohen1997 I have try to draw code Scanner Border only when detected code const topLeftX = codesFrame.x; const topLeftY = codesFrame.y; const topRightX = codesFrame.x + codesFrame.width; const topRightY = codesFrame.y; const bottomRightX = codesFrame.x + codesFrame.width; const bottomRightY = codesFrame.y + codesFrame.height; const bottomLeftX = codesFrame.x; const bottomLeftY = codesFrame.y + codesFrame.height; const paths = [ { d: M${topLeftX + codesFrame.width / 3} ${topLeftY} H${topLeftX} V${topLeftY + codesFrame.height / 3} }, { d: M${topRightX - codesFrame.width / 3} ${topRightY} H${topRightX} V${topRightY + codesFrame.height / 3} }, { d: M${bottomRightX - codesFrame.width / 3} ${bottomRightY} H${bottomRightX} V${bottomRightY - codesFrame.height / 3} }, { d: `M${bottomLeftX + codesFrame.width / 3} ${bottomLeftY} H${bottomLeftX} V${bottomLeftY divide for 3 just my calculate the stroke width: The Result is:

image

can you send the full snippet or your code?

@shacharcohen1997 I think this well help

image image
inkCrazy commented 4 months ago

refre to:https://medium.com/@susumtang/improve-user-experience-with-barcode-highlight-box-using-react-native-vision-camera-code-scanner-ff7f67089aab

android: 5731720667763_ pic_hd

iOS: 5741720667766_ pic_hd

ios device correct√ android device Coordinate shift x

tuannha01 commented 4 months ago

refre to:https://medium.com/@susumtang/improve-user-experience-with-barcode-highlight-box-using-react-native-vision-camera-code-scanner-ff7f67089aab

android: 5731720667763_ pic_hd

iOS: 5741720667766_ pic_hd

ios device correct√ android device Coordinate shift x

Can you share your code to discuss more?

inkCrazy commented 4 months ago

refre to:https://medium.com/@susumtang/improve-user-experience-with-barcode-highlight-box-using-react-native-vision-camera-code-scanner-ff7f67089aab android: 5731720667763_ pic_hd iOS: 5741720667766_ pic_hd ios device correct√ android device Coordinate shift x

Can you share your code to discuss more?

import {useFocusEffect, useIsFocused} from '@react-navigation/native';

import React, {useEffect, useState, useRef} from 'react';
import {
  StyleSheet,
  Dimensions,
  Text,
  TouchableWithoutFeedback,
  Image,
  NativeModules,
  StatusBar,
  Platform,
  AppState,
  BackHandler,
  DrawerLayoutAndroid,
  Linking,
  PermissionsAndroid,
  View,
  Pressable,
} from 'react-native';
import ImagePicker from 'react-native-image-crop-picker';
import RNQRGenerator from 'rn-qr-generator';
import ToastUtils from '../utils/ToastUtils';
import ComUtils from '../utils/ComUtils';
import BarcodeMask from '../components/BarcodeMask';
import {useHeaderHeight} from '@react-navigation/elements';
import {
  Camera,
  useCameraDevice,
  NoCameraDeviceError,
  useCodeScanner,
} from 'react-native-vision-camera';
import DeviceInfo from 'react-native-device-info';

const BarCodeScanner = ({navigation, route}) => {
  const {StatusBarManager} = NativeModules;
  const statusBarHeight = StatusBarManager.HEIGHT; //状态栏高度:ios54 android32
  const navigationBarHeader = useHeaderHeight(); //ios103, android 56
  const {height, width} = Dimensions.get('window');
  // const CAM_VIEW_HEIGHT = Dimensions.get('window').height;
  const CAM_VIEW_HEIGHT =
    height -
    (Platform.OS === 'ios' ? statusBarHeight : statusBarHeight) -
    navigationBarHeader;
  const CAM_VIEW_WIDTH = Dimensions.get('window').width;
  // The following are based on landscape orientation with home button to the right
  //andorid全面屏幕中 Dimensions.get('window').height 计算屏幕高度时会自动减少StatusBar 的高度
  const maskWidth = width * 0.7;
  // left
  const topMargin =
    (height -
      (Platform.OS === 'ios' ? statusBarHeight : 0) -
      navigationBarHeader -
      maskWidth) /
    2;
  // top
  const leftMargin = (width - maskWidth) / 2;
  // width (height of portrait orientation)
  const frameWidth = maskWidth;
  // height (width of portrait orientation)
  const frameHeight = maskWidth;
  //TM的计算的百分比
  const scanAreaX = leftMargin / CAM_VIEW_HEIGHT;
  const scanAreaY = topMargin / CAM_VIEW_WIDTH;
  const scanAreaWidth = frameWidth / CAM_VIEW_HEIGHT;
  const scanAreaHeight = frameHeight / CAM_VIEW_WIDTH;
  //参考地址:https://github.com/react-native-camera/react-native-camera/pull/2719/files/583507f22363d330f903af0215f20250a7ee3851#diff-518dcc98e7451b35bc3b9acfc686894d2bee38b6ced2d9c59ccec56153283f5d

  const device = useCameraDevice('back');
  const [layout, setLayout] = useState({x: 0, y: 0, width: 0, height: 0});
  const [scanFrame, setScanFrame] = useState({height: 1, width: 1});
  const [codeScannerHighlights, setCodeScannerHighlights] = useState([]);

  const cameraRef = useRef(null);
  const mBarCodeRead = useRef(false);
  const isFocused = useIsFocused();
  const [paths, setPaths] = useState([]);

  const [lightSource, setLightSource] = useState(
    require('../res/img/scanLigtOff.png'),
  );
  const scanFromPhoto_img = require('../res/img/scan_from_phone.png');
  const [flashMode, setFlashMode] = useState('off');
  const [codes, setCodes] = useState([]);

  const codeScanner = useCodeScanner({
    codeTypes: ['qr', 'ean-13'],
    // regionOfInterest: {
    //   x: leftMargin,
    //   y: topMargin,
    //   width: frameWidth,
    //   height: frameHeight,
    // },

    onCodeScanned: (codes, frame) => {
      console.log('codes===', codes, frame);
      setScanFrame(frame);
      setCodeScannerHighlights(
        codes.map(code => ({
          value: code.value,
          height: code.frame?.width ?? 0,
          width: code.frame?.height ?? 0,
          x: code.frame?.y ?? 0,
          y: code.frame?.x ?? 0,
        })),
      );
    },

    // onCodeScanned: codes => {
    //   console.log(codes && codes[0].value);
    //   // [{"corners": [[Object], [Object], [Object], [Object]], "frame": {"height": 43, "width": 48, "x": 141, "y": 302}, "type": "qr", "value": "HN19004070ANS7"}]
    //   //[{"x": 228, "y": 227}, {"x": 266, "y": 228}, {"x": 267, "y": 264}, {"x": 229, "y": 263}]
    //   console.log('onCodeScanned', codes, codes && codes[0].corners);
    //   setCodes(codes);

    //   return;
    //   if (codes && codes[0].value) {
    //     onBarCodeRead(codes[0].value);
    //   }
    // },
  });

  // eslint-disable-next-line react/no-unstable-nested-components
  const HightlightBox = ({highlight, layout, scanFrame}) => {
    console.log('highlightBox', highlight, layout, scanFrame);
    return (
      <Pressable onPress={() => onBarCodeRead(highlight.value)}>
        <View
          style={[
            {
              position: 'absolute',
              borderWidth: 2,
              borderColor: 'green',
            },
            {
              right: highlight.x * (layout.width / scanFrame.height),
              top: highlight.y * (layout.height / scanFrame.width),
              height: highlight.height * (layout.width / scanFrame.height),
              width: highlight.width * (layout.height / scanFrame.width),
            },
          ]}
        />
      </Pressable>
    );
  };

  const onBarCodeRead = result => {
    console.log('onBarCodeRead', result);
    if (!mBarCodeRead.current) {
      mBarCodeRead.current = true;
      navigation.pop();
      if (route.params?.callback) {
        route.params?.callback(result);
      }
    }
  };

  useEffect(() => {
    if (Platform.OS === 'android') {
      requestCameraPermission();
    }

    // const fors = AppState.addEventListener('focus', () => {
    //   console.log('进来了');

    //   if (Platform.OS === 'android') {
    //     requestCameraPermission();
    //   }
    // });

    return () => {
      // fors.remove();
    };
  }, []);

  const requestCameraPermission = async () => {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.CAMERA,
    );

    if (granted === PermissionsAndroid.RESULTS.GRANTED) {
      console.log('开启摄像头成功');
    } else {
      ToastUtils.info('请开启摄像头权限');
      navigation.pop();
      Linking.openSettings();
    }
  };

  const openPicker = () => {
    ImagePicker.openPicker({
      compressImageQuality: 0.1, //以质量压缩图像(从0到1,其中1为最佳质量)。在iOS上,大于0.8的值在大多数图像中不会产生明显的质量提高,而大于0.8的值将使文件大小比减小1的大小减少一半或更少。
      showsSelectedCount: true, //是否显示所选资产的数量 仅iOS
      cropperChooseText: '完成', //仅iOS
      cropperCancelText: '取消', //仅iOS
      // includeBase64: true, //是否在结果中包含base64数据
      multiple: false,
      cropping: true,
    })
      .then(async image => {
        console.log('openPicker', image);
        let url = null;
        if (Platform.OS === 'android') {
          url = image.path;
        } else {
          url = image.sourceURL;
        }
        RNQRGenerator.detect({
          uri: url,
        })
          .then(response => {
            const {values} = response; // Array of detected QR code values. Empty if nothing found.
            console.log('QR code values', values);
            if (values && values.length > 0) {
              onBarCodeRead({data: values[0]});
            } else {
              ToastUtils.show('请扫描清晰的二维码');
            }
          })
          .catch(error => console.log('Cannot detect QR code in image', error));
      })
      .catch(error => {
        console.log('openPicker error', error);
      });
  };

  const scanFromPhoto = () => {
    //从手机相册选择
    if (Platform.OS === 'android') {
      if (Platform.Version >= 33) {
        ComUtils.checkAndRequestPermission(
          PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
          () => {
            openPicker();
          },
          () => {},
          '需要您的相册权限用于二维码图片,若不允许,将无法选择图片',
        );
      } else {
        ComUtils.checkAndRequestPermission(
          PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
          () => {
            openPicker();
          },
          () => {},
          '需要您的相册权限用于二维码图片,若不允许,将无法选择图片',
        );
      }
    } else {
      openPicker();
    }
  };

  const renderBottomView = () => {
    return (
      <View
        style={{
          flex: 1,
          alignSelf: 'stretch',
          alignItems: 'center',
          justifyContent: 'space-around',
        }}>
        <Text style={{fontSize: 14, color: '#fff', marginTop: 10}}>
          将二维码/条码放入框内,即可自动扫描
        </Text>
        <View
          style={{
            flexDirection: 'row',
            justifyContent: 'space-between',
            width: width * 0.7,
          }}>
          <TouchableWithoutFeedback onPress={handleFlashModeChange}>
            <Image source={lightSource} />
          </TouchableWithoutFeedback>
          <TouchableWithoutFeedback onPress={scanFromPhoto}>
            <Image source={scanFromPhoto_img} />
          </TouchableWithoutFeedback>
        </View>
      </View>
    );
  };

  const handleFlashModeChange = () => {
    if (cameraRef.current) {
      if (flashMode === 'on') {
        setFlashMode('off');
        setLightSource(require('../res/img/scanLigtOff.png'));
      } else {
        setFlashMode('on');
        setLightSource(require('../res/img/scanLightOn.png'));
      }
    }
  };

  if (device == null) {
    return <NoCameraDeviceError />;
  }

  let qrCodeTagControl = null;
  if (codes && codes.length > 0) {
    qrCodeTagControl = (
      <View style={StyleSheet.absoluteFill}>
        {codes.map((item, index) => {
          return <QRCodeTag key={index} codesFrame={item.frame} />;
        })}
      </View>
    );
  }

  const onLayout = evt => {
    if (evt.nativeEvent.layout) {
      setLayout(evt.nativeEvent.layout);
    }
  };

  return (
    <View style={{flex: 1, height: '100%', width: '100%'}}>
      <Camera
        ref={cameraRef}
        photo={false}
        video={false}
        isActive={isFocused}
        resizeMode="cover"
        onLayout={onLayout}
        // style={{
        //   height: frameWidth,
        //   width: frameHeight,
        //   position: 'absolute',
        //   left: leftMargin,
        //   top: topMargin,
        // }}
        torch={flashMode}
        onError={error => {
          // console.error(error)
        }}
        style={StyleSheet.absoluteFill}
        device={device}
        codeScanner={codeScanner}
      />
      <BarcodeMask
        edgeColor={'#22ff00'}
        edgeBorderWidth={3}
        animatedLineColor={'#22ff00'}
        animatedLineHeight={1}
        lineAnimationDuration={3000}
        transparency={0.2}
        width={frameWidth}
        height={frameHeight}
        renderBottomView={renderBottomView}
      />
      {codeScannerHighlights.map((hightlight, key) => (
        <HightlightBox
          key={key}
          highlight={hightlight}
          layout={layout}
          scanFrame={scanFrame}
        />
      ))}
      {/* {qrCodeTagControl} */}
    </View>
  );
};

//[Figuring out dimensions in CodeScanner ]: https://github.com/mrousavy/react-native-vision-camera/issues/2436
//https://github.com/mrousavy/react-native-vision-camera/issues/2736
const QRCodeTag = ({codesFrame}) => {
  console.log('codesFrame===', codesFrame);
  const topLeftX = codesFrame.x;
  const topLeftY = codesFrame.y;
  const topRightX = codesFrame.x + codesFrame.width;
  const topRightY = codesFrame.y;
  const bottomRightX = codesFrame.x + codesFrame.width;
  const bottomRightY = codesFrame.y + codesFrame.height;
  const bottomLeftX = codesFrame.x;
  const bottomLeftY = codesFrame.y + codesFrame.height;

  const minY = Math.min(topLeftY, topRightY, bottomLeftY, bottomRightY);
  const maxY = Math.max(topLeftY, topRightY, bottomLeftY, bottomRightY);
  const minX = Math.min(topLeftX, topRightX, bottomLeftX, bottomRightX);
  const maxX = Math.max(topLeftX, topRightX, bottomLeftX, bottomRightX);

  const width = maxX - minX;
  const height = maxY - minY;

  return (
    <View
      style={{
        position: 'absolute',
        left: minX,
        top: minY,
        width: codesFrame.width,
        height: codesFrame.height,
        borderWidth: 2,
        borderColor: 'green',
      }}
    />
  );
};

export default BarCodeScanner;
System:
  OS: macOS 14.5
  CPU: (10) arm64 Apple M2 Pro
  Memory: 92.67 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.0.0
    path: /var/folders/kb/p5p48sk13csbptrctf07nlt00000gn/T/yarn--1720674280836-0.11255958323586901/node
  Yarn:
    version: 1.22.19
    path: /var/folders/kb/p5p48sk13csbptrctf07nlt00000gn/T/yarn--1720674280836-0.11255958323586901/yarn
  npm:
    version: 9.8.1
    path: /opt/homebrew/bin/npm
  Watchman:
    version: 2024.01.22.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.14.3
    path: /Users/inkcrazy/.rvm/gems/ruby-3.0.0/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.5
      - iOS 17.5
      - macOS 14.5
      - tvOS 17.5
      - visionOS 1.2
      - watchOS 10.5
  Android SDK:
    API Levels:
      - "28"
      - "29"
      - "30"
      - "31"
      - "32"
      - "33"
      - "33"
      - "34"
    Build Tools:
      - 28.0.3
      - 29.0.2
      - 29.0.3
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 33.0.1
      - 33.0.2
      - 34.0.0
      - 34.0.0
    System Images:
      - android-31 | Android TV ARM 64 v8a
      - android-33 | Google APIs ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-UpsideDownCake | Google APIs ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2022.1 AI-221.6008.13.2211.9619390
  Xcode:
    version: 15.4/15F31d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 11.0.22
    path: /Users/inkcrazy/.sdkman/candidates/java/current/bin/javac
  Ruby:
    version: 3.0.0
    path: /Users/inkcrazy/.rvm/rubies/ruby-3.0.0/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.2
    wanted: 0.74.2
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false