Using react-native-webview and camera

0

Introduction:

Hey guys! I’m developing a project that basically consists of a webview, with integrated navigation and login screen, however this webview in two execution times needs to read qr code or barcode. Using android and ios permissions before opening the webview, I was able to access the camera performing library implementations only on the server side, however I found that on ios the camera crashed on safari and my webview was not detected, on android it worked normally, although couldn’t make the camera names appear.

What I thought:

I thought Find a QR and barcode reader library compatible with JAVA and Primefaces 8 with webview support, being deployed on the server side, or during the execution of the webview, identify the url where the QR reading would be used, pause the webview and open a react-native library to scan the QR code, after that the app would return to webview giving a refrash to the page passing the necessary parameters in the url

The problem:

The problem with all this is that I have no idea how to implement this switching behavior between react native scanner libraries and the webview. I’m also worried about navigation, after the reading has been performed I can’t reactivate the scanner but go back to the previous page, that is, the page before the scanner. Another issue is the user session that I can’t lose, the user must remain logged in.

My Code:

This page receive url of webservices in my loginPage:

/* import * as React from 'react'; */
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, StyleSheet, BackHandler, Alert, StatusBar, Platform, Linking } from 'react-native';
import { WebView } from 'react-native-webview';
import { ActivityIndicator } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import {check, PERMISSIONS, request, RESULTS} from 'react-native-permissions';

export default function WebViewUsrLogado(props:any) {

  /* ==========================> Variaveis utilizadas <========================= */
  
  const propriedade = props.route.params.url
  const webViewRef = useRef<any | null>(null)
  const statusBarRef = useRef<any | null>(null)
  const [cameraGranted, setCameraGranted] = useState(false);
  const [newurl, setNewUrl] = useState<any | null>(null);
  const navigation = useNavigation();
  const isIOS = Platform.OS === 'ios' ? true : false
;  const Spinner = () => (
    <View style={styles.activityContainer}>
      <ActivityIndicator size="large" color="#f29900" />
    </View>
  );
  const handleCameraPermission = async () => {
    const permission = Platform.OS === 'ios' ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA
    console.log(permission)
    const res = await check(permission);
    if (res === RESULTS.GRANTED) {
      setCameraGranted(true);
    } else if (res === RESULTS.DENIED) {
      const res2 = await request(permission);
      res2 === RESULTS.GRANTED ? setCameraGranted(true) : setCameraGranted(false);
    }    
  };

  const handleAcessExternalStoragePermission = async () => {
    const permission = Platform.OS === 'ios' ? PERMISSIONS.IOS.PHOTO_LIBRARY : PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE
    console.log(permission)
    const res = await check(permission);
    if (res === RESULTS.GRANTED) {
      setCameraGranted(true);
    } else if (res === RESULTS.DENIED) {
      const res2 = await request(permission);
      res2 === RESULTS.GRANTED ? setCameraGranted(true) : setCameraGranted(false);
    }    
  };

  const handlePhotoLibraryAddPermission = async () => {
    const permission = PERMISSIONS.IOS.PHOTO_LIBRARY_ADD_ONLY
    console.log(permission)
    const res = await check(permission);
    if (res === RESULTS.GRANTED) {
      console.log( `Permitido`)
      setCameraGranted(true);
    } else if (res === RESULTS.DENIED) {
      const res2 = await request(permission);
      res2 === RESULTS.GRANTED ? setCameraGranted(true) : setCameraGranted(false);
    }    
  };

  const handleMicrophonePermission = async () => {
    const permission = PERMISSIONS.IOS.MICROPHONE
    console.log(permission)
    const res = await check(permission);
    if (res === RESULTS.GRANTED) {
      setCameraGranted(true);
    } else if (res === RESULTS.DENIED) {
      const res2 = await request(permission);
      res2 === RESULTS.GRANTED ? setCameraGranted(true) : setCameraGranted(false);
    }    
  };
  
  /* ==============================> Observaveis <============================= */

  //Verificação de permissão
  useEffect(() => {
    handleCameraPermission();
    handleAcessExternalStoragePermission();
    if (Platform.OS == `ios`) {
      handlePhotoLibraryAddPermission();
      handleMicrophonePermission();
    }
  }, []);

  //Botão voltar
  useEffect(() => {

    const backAction = () => {
      const url = webViewRef.current.startUrl;
      if (url.includes('/home.xhtml')) {
        console.log('Peguei')
        alerta();
      }
      webViewRef.current.goBack();
      return true;
    };
    const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
    return () => backHandler.remove();
  }, []);

  /* ===============================> Funcções <=============================== */

  const backButtonHandler = () => {
    console.log(newurl)
    if (webViewRef.current) {
      if (newurl.includes('/home.xhtml')) {
        alerta()
      } else {
        webViewRef.current.goBack()
      }
    }
  }
  
  function alerta(){
    Alert.alert(
      "Deseja Sair?",
      "Caso deseja sair sua sessão e atividades serão encerradas.",
      [
        {
          text: "Cancel",
          onPress: () => console.log("Cancel Pressed"),
          style: "cancel"
        },
        { text: "OK", onPress: () => logOut() }
      ]
    );
  }
  
  //Verifica se a url é igual a url de login, caso falso chama logout
  function voltar(url: any){
    setNewUrl(url)
    if (url == 'https:/myurl/login.xhtml') {
      console.log('iGUAL');
      logOut();
    } else if (url.includes(`http://fileurl/`)){
      console.log(`opa peguei <==================================================`)
      Linking.openURL(url);
      webViewRef.current.goBack();
    } else {
      console.log(url)
    }
  }

  //Verifica se existe item na pilha navegação e retira
  function logOut(){
    let canGoBack = navigation.canGoBack();
    if (canGoBack) {
      navigation.goBack();
    } else{
      console.log('tENTOU FAZER AÇÃO MAIS DE UMA VEZ')
    }
  }

  /* ========================> Retorno de Visualização <======================== */
  if (cameraGranted) {
    return (
      <View style={styles.container}>
        <StatusBar backgroundColor="#1c4154" />
        {
          isIOS &&
          <Text onPress={backButtonHandler}>Voltar</Text>
        }
        <WebView
          source={{ uri: propriedade }}
          ref={webViewRef}
          style={styles.view}
          originWhitelist={['*']}
          allowsInlineMediaPlayback
          javaScriptEnabled
          scalesPageToFit
          mediaPlaybackRequiresUserAction={false}
          javaScriptEnabledAndroid
          useWebkit
          startInLoadingState={true}
          renderLoading={Spinner}
          onNavigationStateChange={(event)=>{
            voltar(event.url)
          }}
        />
      </View>
    );
  } else {
    return <Text>No access to camera</Text>;
  }
}

/* ==============================> Estilização <============================= */

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor:'#1c4154',
    paddingTop:20
  },
  activityContainer: {
    alignItems: 'center',
    justifyContent: 'center',
    position: 'absolute',
    top: 0,
    left: 0,
    backgroundColor: '#fff',
    height: '100%',
    width: '100%'
  },
  view: {
    borderColor: 'red',
  }
});

Android Manifest

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
    <uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
    <uses-permission android:name="android.permission.CAMERA" android:required="false" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    <uses-feature android:name="android.hardware.camera.front" android:required="false" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Development environment:

Website: JAVA 8 and Primefaces 8

APP: “react”: “17.0.1”, “react-native”: “0.64.2”,