使用 react-native-webview 和相机

Using react-native-webview and camera

简介:

大家好!我正在开发一个项目,该项目基本上由一个带有集成导航和登录屏幕的 webview 组成,但是这个 webview 在两次执行时需要读取二维码或条形码。在打开 webview 之前使用 android 和 ios 权限,我只能在服务器端访问执行库实现的相机,但是我发现在 ios 上相机在 safari 上崩溃了并且未检测到我的 webview,在 android 它工作正常,但无法显示相机名称。

我的想法:

我想找到一个 QR 和条形码 reader 库与 JAVA 和 Primefaces 8 兼容,支持 webview,部署在服务器端,或者在 webview 执行期间,识别 url 在使用 QR 读取的地方,暂停 webview 并打开一个 react-native 库来扫描 QR 码,之后应用程序将 return 到 webview 刷新页面并传递必要的参数在 url

问题:

所有这一切的问题是我不知道如何在反应原生扫描器库和 webview 之间实现这种切换行为。 我也担心导航,在执行阅读后我无法重新激活扫描仪,而是返回上一页,即扫描仪之前的页面。 另一个问题是我不能丢失的用户会话,用户必须保持登录状态。

我的代码:

此页面在我的登录页面中收到 url 个网络服务:

/* 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 清单

    <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" />

开发环境:

网站:JAVA 8 和 Primefaces 8

应用程序: “反应”:“17.0.1”, “本机反应”:“0.64.2”,

经过大量搜索,我想出了一个解决方法。在服务器端,当我需要读取 url 时,我会生成一个伪造的 url 传递参数。在参数中我使用了两个:mustReadBrcodeOrQR 和 type(Barcode/qr)。在客户端,我使用了 react-native-webview 的 onShouldStartLoadWithRequest 属性,它调用了一个传递 url 的方法,当 url 具有这些参数时,我调用了一个模式,其中我打开代码 reader debarras/qr,阅读后我通过网络服务发送结果并重新加载页面,该页面现在将由网络服务

填写字段