rodgomesc / vision-camera-code-scanner

VisionCamera Frame Processor Plugin to read barcodes using MLKit Vision QrCode Scanning
MIT License
338 stars 222 forks source link

The camera flickers when using frameProcessor #152

Open Texicitys opened 1 year ago

Texicitys commented 1 year ago

Hello !

I'm trying vision-camera-code-scanner package because react-native-qrcode-scanner is no longuer active. Everything worked without issues with the react-native-qrcode-scanner. But now, the camera flickers when I activate the frameProcessor.

Here is my code :

import React from 'react'
import { AppRegistry, TouchableOpacity, Linking, Dimensions, StyleSheet } from 'react-native'
import {  Factory, View, ScrollView, Flex, Center, Text, Spinner, Box, VStack, HStack, Avatar, Image, IconButton, Actionsheet, Heading, Button, useDisclose, useToast } from 'native-base';
import { Trans, useTranslation } from 'react-i18next';
import { WebView } from 'react-native-webview';
import Icon from 'react-native-vector-icons/FontAwesome';

import { DataContext } from 'contexts/DataContext'

import LoadScreen from 'screens/LoadScreen'

import { Camera } from 'react-native-vision-camera';
import { useCameraDevices } from 'react-native-vision-camera';

import { useScanBarcodes, BarcodeFormat } from 'vision-camera-code-scanner';

import UserHeader from "components/layout/UserHeader"

export default function NewScreen(props) {
  const { t } = useTranslation();

  const {mainDataState, authDispatch, refreshMainDataState, pageDataState, setPage, fetchPath} = React.useContext(DataContext)
  // On le met false quand l'utilisateur a accepté qu'on lui demander d'accéder à sa camera
  const [isWelcomeMessage, setIsWelcomeMessage] = React.useState(true);
  // On le met lorsqu'un QR code est en cours d'analyse
  const [isLoadingQR, setIsLoadingQR] = React.useState(false);
  // On le met lorsqu'on a vu que le QR code lie bien à un calendrier
  const [calendarId, setCalendarId] = React.useState(0);
  // On enregistre le secret_key
  const [secretKey, setSecreteKey] = React.useState('');
  // On le met lorsque le membre a bien join le calendrier
  const [calendarMemberIsCreated, setCalendarMemberIsCreated] = React.useState(false);
  // On le valide lorsque le réglement doit être validé
  const [acceptedRules, setAcceptedRules] = React.useState(false);
  // Lorqu'on est en train de rejoindre le calendrier
  const [isJoiningCalendar, setIsJoiningCalendar] = React.useState(false);
  // Checker les permissions de la CAM
  const [cameraPermission, setCameraPermission] = React.useState(false);

  const devices = useCameraDevices();
  const device = devices.back;
  const FactoryCamera = Factory(Camera);

  const [frameProcessor, barcodes] = useScanBarcodes([BarcodeFormat.QR_CODE], {
    checkInverted: true,
  });

  React.useEffect(() => {
    Camera.getCameraPermissionStatus().then(setCameraPermission);
  }, []);

  // console.log(`Re-rendering Navigator. Camera: ${cameraPermission}`);

  const toast = useToast();

  React.useEffect(() => {
    if (calendarMemberIsCreated) {
      setCalendarId(0)
      // Add main path to get info about user
      refreshMainDataState()
    }
  }, [calendarMemberIsCreated]);

  // React.useEffect(() => {
  //   if (cameraPermission === 'authorized' && !isWelcomeMessage) {
  //     console.log(`Camera permission status has been authorized. We will now use camera to search QR code`);
  //     console.log(`We search for the camera device.`);
  //     // const devices = useCameraDevices();
  //     const device = devices.back;
  //   }
  // }, [cameraPermission, isWelcomeMessage]);

  const openLink = (url) => {
    Linking.openURL(url).catch(err => console.error("Couldn't load page", err));
  };

  React.useEffect(() => {
    if (pageDataState.data && isLoadingQR === true && pageDataState.data.calendar_member_accepted !== undefined) {

      if (pageDataState.data.calendar_member_accepted) {
        console.log("C'est confirmé, le code QR fonctionne !")
        setCalendarId(pageDataState.data.calendar.id)
        // setCalendarInfos({
        //   id: pageDataState.data.calendar.id,
        //   secret_key: pageDataState.data.calendar.secret_key,
        //   full_address: pageDataState.data.calendar.full_address,
        //   short_to_s: pageDataState.data.calendar.short_to_s,
        //   rules:  pageDataState.data.calendar.rules
        // })
        // On vérifie si on a besoin que le membre accepte les règles
        if ((pageDataState.data.calendar.rules && pageDataState.data.calendar.rules.length>0) && !acceptedRules) {
          console.log("Il faut d'abord accepter le réglement.")
          // Si il a besoin, on lui affiche les règles
          toast.show({
            title: t("calendar_members.new.accepted_qr_code_title"),
            description: t("calendar_members.new.accepted_qr_code"),
            status: 'success'
          });
        } else {
          joiningCalendar();
        }
      } else {
        console.log("Ce code QR de buanderie ne semble plus fonctionner.")

        setCalendarId(0)

        toast.show({
          title: t("calendar_members.new.unaccepted_qr_code_title"),
          description: t("calendar_members.new.unaccepted_qr_code"),
          status: 'error'
        });
      }
    } else if (pageDataState.data == null && isLoadingQR == true) {
      setCalendarId(0)
      toast.show({
        title: t("calendar_members.new.unaccepted_calendar_title"),
        description: t("calendar_members.new.unaccepted_calendar"),
        status: 'error'
      });
    }
    setIsLoadingQR(false)
  }, [pageDataState]);

  function acceptRules() {
    setAcceptedRules(true)
    joiningCalendar();
  }

  if (barcodes.length !== 0 && isLoadingQR === false && calendarId === 0 && isJoiningCalendar === false) {
    console.log('On a detecté un nouveau code QR.')
    // console.log(barcodes)
    onSuccess(barcodes[barcodes.length - 1])
  }

  function onSuccess(e) {
    setIsLoadingQR(true)

    // Recevoir les infos de l'URL
    // const url = new URL("https://calmanager.com/invitation/1/ZZ8UT2");
    const split_url = e.displayValue.split('/')
    const path = split_url.slice(3, split_url.length)

    // Si le path a l'air correct
    // Exemple : ["invitation", "1", "OBAC65"]
    if (path[0] == "invitation" && !isNaN(path[1]) && path[2]) {
      console.log("Le code QR semble correct.")
      setSecreteKey(path[2])
      // const calendarId = path[1]
      // const calendarSecretKey = path[2]
      const pathname = path.join("/")
      // setPage(pathname)
      // fetchPath("invitation/1/ZZ8UT2")
      setPage(pathname)
      // setPage("invitation/1/ZZ8UT2")
      // scanner.reactivate()
    } else {
      console.log("Le code QR ne semble pas correct.")
      setCalendarId(0)
      toast.show({
        title: t("calendar_members.new.unaccepted_qr_code_title"),
        description: t("calendar_members.new.wrong_qr_code"),
        status: 'error'
      });
      setIsLoadingQR(false)
    }
  }
  // const onSuccess = e => {
  //   console.log(e.data)
  //
  //
  //   // Linking.openURL(e.data).catch(err =>
  //   //   console.error('An error occured', err)
  //   // );
  // };

  function joiningCalendar() {
    console.log("Tentative de création du membre de la buanderie...")
    setIsJoiningCalendar(true)
    // Sinon, on essaie de rejoindre la buanderie
    // API call to join calendar
    const fetchData = async () => {
      let response = await fetchPath(`calendars/${pageDataState.data.calendar.id}/calendar_members`, {
        method: 'post',
        data: {
          calendar_member: {
            terms_of_service: true,
            secret_key: secretKey,
            joined_method: "by_app"
            // role: "tenant", // Default
          }
        }
      });
      // console.log(response.data)
      if (response.status == 201) {
        console.log("Membre ajouté avec succès à la buanderie...")
        toast.show({
          title: t("calendar_members.create.success_title"),
          description: t("calendar_members.create.success"),
          duration: 10000,
          status: 'success'
        });
        setCalendarMemberIsCreated(true)
      } else {
        console.log("Erreur lors de la création du membre...")

        toast.show({
          title: t("calendar_members.create.error_title"),
          description: t("calendar_members.create.error"),
          duration: 10000,
          status: 'danger'
        });
      }
      // refresh()
    }
    fetchData()
  }

  const handleRequestCameraPermission = async () => {
    const requestCameraPermission = async () => {
      console.log('Requesting camera permission...');
      const permission = await Camera.requestCameraPermission();
      if (permission === 'authorized') setIsWelcomeMessage(false)
      console.log(`Camera permission status: ${permission}`);
      if (permission === 'denied') await Linking.openSettings();
      setCameraPermission(permission);
    }
    requestCameraPermission();
  }

  // if (cameraPermission === 'authorized') {
  //   // console.log(`We search for the camera device.`);
  //   // const devices = useCameraDevices();
  //   // const device = devices.back;
  // }

  // On clique sur Activer ma caméra

  // const showPermissionsPage = cameraPermission !== 'authorized'

  return (
    <ScrollView>
      <Box safeArea>
        <UserHeader navigation={props.navigation}/>
        <Box>
        { isLoadingQR ?
            <Center mt="4">
              <Spinner size="lg" />
              <Text>{t("calendar_members.new.scanning_qr")}</Text>
            </Center>
          :
            calendarId ?
              <Box p={3}>
                <Heading>{t("calendar_members.create.h1", { calendar: pageDataState.data.calendar.short_to_s } )}</Heading>
                <Text fontWeight="bold" mt="3">{t("calendar_members.create.full_address")}</Text>
                <Text>{pageDataState.data.calendar.full_address}</Text>
                { (pageDataState.data.calendar.rules && pageDataState.data.calendar.rules.length>0) ?
                    <>
                      <Text mt="3" fontWeight="bold">{t("calendar_members.create.rules")}</Text>
                      <Box mt="2" shadow={5} borderRadius='md' border={1} style={{ width: Dimensions.get('window').width-24, height: 300 }}>
                        <WebView
                          originWhitelist={['*']}
                          source={{ html: "<html><head><meta name='viewport' content='width=device-width, initial-scale=1.0'></head><body>" + pageDataState.data.calendar.rules + "</body></html>" }}
                        />
                      </Box>
                    </>
                  : []
                }
                { isJoiningCalendar ?
                    <Box mt="3">
                      <Spinner size="lg" />
                      <Text textAlign="center">{t("calendar_members.create.joining_calendar")}</Text>
                    </Box>
                  :
                    <Button colorScheme="green" size="lg" mt="5" onPress={() => acceptRules()}>
                      {t("calendar_members.create.confirm")}
                    </Button>
                }
              </Box>
            :
              isWelcomeMessage ?
                <Box p={3}>
                  <Heading>{t("calendar_members.new.explications.title", {username: mainDataState.data.current_user.first_name})}</Heading>
                  <VStack mt="4" space="3">
                    <Text fontSize="20" textAlign="center">
                      {t("calendar_members.new.explications.welcome")}
                    </Text>
                    <Text fontSize="20" textAlign="center">
                      {t("calendar_members.new.explications.please_scan")}
                    </Text>
                    <Text fontSize="20" textAlign="center">
                      {t("calendar_members.new.explications.your_calendar")}
                    </Text>
                    <Text fontSize="20" textAlign="center">
                      {t("calendar_members.new.explications.access_to_calendar")}
                    </Text>
                    <Box alignItems="center">
                      <Image alt="QR-code" source={require('images/scan-qr-code.jpg')} />
                    </Box>
                    <Button size="lg" leftIcon={ <Icon name='camera' color="black" size={20} /> } onPress={() => handleRequestCameraPermission()}>
                      {t("calendar_members.new.explications.accept_camera")}
                    </Button>
                    <Text mt="10" textAlign="center">
                      {t("calendar_members.new.explications.no_qr_code")}
                    </Text>
                    <Text textAlign="center" onPress={() => openLink("https://calmanager.com")} color="primary.600">
                      {t("calendar_members.new.explications.access_to_website")}
                    </Text>
                  </VStack>
                </Box>
              :
                (device == null) ?
                  <Center mt="4">
                    <Spinner size="lg" />
                  </Center>
                :
                  <Box p={3}>
                    <Heading>{t("calendar_members.new.scan_title")}</Heading>
                    <Text>{t("calendar_members.new.scan_description")}</Text>
                    <FactoryCamera
                      mt="3"
                      height={300}
                      width={"100%"}
                      device={device}
                      isActive={true}
                      frameProcessor={frameProcessor}
                      frameProcessorFps={2}
                    />
                  </Box>
        }
        </Box>
      </Box>
    </ScrollView>
  )
}

const styles = StyleSheet.create({
  camera: {
    // flex: 1,
    width: "100%",
    height: 300,
  }
});

When I comment those two lines :

frameProcessor={frameProcessor}
frameProcessorFps={2}

The camera stop to flicker (but no QR code is scanned).

Here is a video of the problem : https://github.com/rodgomesc/vision-camera-code-scanner/assets/5115583/942e9202-a097-4a8a-b852-71a24fd815d5

Here is my package.json :

{
  "name": "CalmanagerApp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint ."
  },
  "dependencies": {
    "@invertase/react-native-apple-authentication": "^2.2.2",
    "@react-native-async-storage/async-storage": "^1.17.11",
    "@react-native-google-signin/google-signin": "^9.0.2",
    "@react-navigation/bottom-tabs": "^6.5.2",
    "@react-navigation/native": "^6.1.0",
    "@react-navigation/stack": "^6.3.10",
    "axios": "^1.2.1",
    "i18next": "^22.1.5",
    "native-base": "^3.4.25",
    "react": "18.1.0",
    "react-hook-form": "^7.41.2",
    "react-i18next": "^12.1.1",
    "react-native": "0.70.6",
    "react-native-calendars": "^1.1293.0",
    "react-native-device-info": "^10.3.0",
    "react-native-fbsdk-next": "^11.1.0",
    "react-native-gesture-handler": "^2.8.0",
    "react-native-get-random-values": "^1.8.0",
    "react-native-keyboard-aware-scroll-view": "^0.9.5",
    "react-native-localize": "^2.2.4",
    "react-native-reanimated": "^2.13.0",
    "react-native-rename": "^3.1.0",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "^3.18.2",
    "react-native-svg": "12.1.1",
    "react-native-vector-icons": "^9.2.0",
    "react-native-vision-camera": "^2.15.2",
    "react-native-webview": "^11.26.0",
    "vision-camera-code-scanner": "https://github.com/Texicitys/vision-camera-code-scanner"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/runtime": "^7.12.5",
    "@react-native-community/eslint-config": "^2.0.0",
    "babel-jest": "^26.6.3",
    "eslint": "^7.32.0",
    "jest": "^26.6.3",
    "metro-react-native-babel-preset": "0.72.3",
    "react-test-renderer": "18.1.0"
  },
  "jest": {
    "preset": "react-native"
  }
}

Thanks for your help !

Texicitys commented 1 year ago

Hello !

I still have no solution. What could I have done wrong ? Do you maybe have a clue to help me?

Thank you very much for your help !