React Native:Webview getUserMedia 不起作用(onPermissionRequest 覆盖?)

Posted

技术标签:

【中文标题】React Native:Webview getUserMedia 不起作用(onPermissionRequest 覆盖?)【英文标题】:React Native: Webview getUserMedia not working (onPermissionRequest override?) 【发布时间】:2018-05-28 10:18:11 【问题描述】:

我正在使用 React Native 开发适用于 iOS、Android 和 Windows 的应用程序,我需要在其中通过 WebView 显示网页。该网页访问设备的摄像头,因此它使用MediaDevices.getUserMedia() javascript 函数。

它在桌面上运行没有问题,即使在智能手机上的 Chrome 应用程序中。但是,当我通过 React Native <WebView /> 调用网页时,我收到了 PermissionDenied 错误。问题是没有显示请求让我接受该许可。它只是拒绝请求而不询问。

这是我的 WebView 元素的示例:

    <WebView 
        style=flex: 1
        mediaPlaybackRequiresUserAction=false
        domStorageEnabled=true
        allowsInlineMediaPlayback=true
        source=uri: 'https://myurltomyapp.com/index.html' 
        startInLoadingState=true
        allowUniversalAccessFromFileURLs=true
    />

我已经在androidManifest.xml 上设置了所有必要的权限(甚至有些不是为了测试而需要的):

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
    <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.RECORD_VIDEO"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

是的,托管网站的 URL 是经过 SSL 认证的,所以它通过 HTTPS。它适用于 Chrome 应用程序,因此它也应该适用于 WebView

我一直在网上阅读,似乎是因为 React Native WebView 缺少 onPermissionRequest() 实现,所以它默默地失败(并否认它)。

有没有办法在 React Native 中实现它覆盖本机 WebView?如果可能,我必须编辑哪些文件?是否有任何第三方模块(最新)已实现(尚未找到)?

谢谢。

【问题讨论】:

你解决了吗?我不使用反应,但试图从 web 视图内部调用 getUserMedia() 并且什么都没有...... @SamuelMéndez 看到我的答案!希望这对你有帮助! 我使用原生 WebView 解决了这个问题。我的问题不是要求用户接受权限,只是调用request.grant() 【参考方案1】:

最后我不得不在原生 Android 中实现一个自己的 WebView 组件。问题是在本机 WebView 中未实现的 onPermissionRequest() 函数。您必须创建自己的并覆盖该功能。就我而言,我必须添加以下代码(所有文件都在路径 [react-native-project]/android/app/src/main/java/com/listproject/permissionwebview/ 下):

PermissionWebviewPackage.java:

package com.listproject.permissionwebview;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;

/**
 * Add this package to getPackages() method in MainActivity.
 */

public class PermissionWebviewPackage implements ReactPackage 

    @Override
    public List<ViewManager> createViewManagers(
            ReactApplicationContext reactContext) 
        return Arrays.<ViewManager>asList(
            new PermissionWebviewViewManager()
          );
    

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) 
        return Collections.emptyList();
    

PermissionWebviewView.java:

package com.listproject.permissionwebview;
import android.content.Context;
import android.widget.LinearLayout;
import android.os.Bundle;
import android.widget.Toast;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.PermissionRequest;
import android.webkit.WebSettings;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.webkit.SslErrorHandler;
import android.support.v4.app.ActivityCompat;
import android.app.LocalActivityManager;
import android.view.ViewGroup;
import android.Manifest;
import android.app.Activity;
import com.listproject.MainActivity;
import com.listproject.R;

public class PermissionWebviewView extends WebView

    private Context context;

    public PermissionWebviewView(Context context) 
        super(context);
        this.context = context;

        this.setWebViewClient(new WebViewClient());

        WebSettings webSettings = this.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);
        webSettings.setMediaPlaybackRequiresUserGesture(false);
        webSettings.setUseWideViewPort(true);
        webSettings.setDomStorageEnabled(true);

        this.setWebChromeClient(new WebChromeClient() 
            @Override
            public void onPermissionRequest(final PermissionRequest request) 
                request.grant(request.getResources());
            
        );
    

PermissionWebviewViewManager.java:

package com.listproject.permissionwebview;

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;

public class PermissionWebviewViewManager extends SimpleViewManager<PermissionWebviewView> 

    public static final String REACT_CLASS = "PermissionWebviewViewManager";
    private String source;

    @Override
    public String getName() 
        return REACT_CLASS;
    

    @Override
    public PermissionWebviewView createViewInstance(ThemedReactContext context) 
        return new PermissionWebviewView(context); //If your customview has more constructor parameters pass it from here.
    

    @ReactProp(name = "sourceUri")
    public void setSource(PermissionWebviewView view, String source) 
        view.loadUrl(source);
    

最后,更新您的MainApplication.java 文件,并将您的包添加到getPackages() 函数中:

@Override
protected List<ReactPackage> getPackages() 
  return Arrays.<ReactPackage>asList(
      new MainReactPackage(),
        [...],
        new PermissionWebviewPackage()
  );

请记住,listprojectpermissionwebview 等的名称可以更改为您需要的任何名称,只要您在文件的命名空间和包引用中更改它们即可。

一旦你有了这一切,你所要做的就是创建 ReactNative 组件导出 ([react-native-project]/app/components/PermissionWebView/index.android.js):

import PropTypes from 'prop-types';
import requireNativeComponent, ViewPropTypes from 'react-native';

// The 'name' property is not important, the important one is below
var mcs = 
  name: 'PermissionWebview',
  propTypes: 
    sourceUri: PropTypes.string,
      ...ViewPropTypes
  
;

module.exports = requireNativeComponent('PermissionWebviewViewManager', mcs);

请注意,我们将 index 的名称设置为 index.android.js,因为该组件是为 Android 制作的,所以它只包含在 Android 平台中。

这样,一切都应该正常工作,并且 WebView 组件应该在使用时请求许可。

编辑:

我已将代码上传到我的Github repository。我只是更改了一些名称,但核心代码没有更改以确保它仍然有效(我无法测试它,所以最好不要更改很多东西)。

希望它对某人有所帮助!

【讨论】:

您可以从 github 或其他地方分享工作样本吗?我在移植此代码时遇到困难 ios 上 getUserMedia 的解决方案你得到了吗? 恐怕不行,抱歉。然而,此时,React Native 可能已经发生了很大的变化,以至于甚至 Android 代码都不再有效。也许他们已经找到了解决此问题的捷径! 这按预期工作。但是injectedJavaScript 属性不起作用。 请发布ios解决方案。【参考方案2】:

在我的情况下,以下代码有效。

第 1 步:向 AndroidManifest.xml 添加权限

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- any additional permissions -->

第 2 步:从“react-native”包中添加“PermissionsAndroid”的导入语句

import 
  PermissionsAndroid
 from 'react-native';

第 3 步:编写异步方法来获取所需的权限,这里我获取的是相机和麦克风权限

cameraPermission = async () => 

    let granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.CAMERA,
      
        title: "Camera Permission",
        message:
          "App needs access to your camera " +
          "so others can see you.",
        buttonNeutral: "Ask Me Later",
        buttonNegative: "Cancel",
        buttonPositive: "OK"
      
    );
    if (granted === PermissionsAndroid.RESULTS.GRANTED) 
      console.log("You can use the camera");
     else 
      console.log("Camera permission denied");
    


micPermission = async () => 

let granted = await PermissionsAndroid.request(
  PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
  
    title: "Audio Permission",
    message:
      "App needs access to your audio / microphone",
    buttonNeutral: "Ask Me Later",
    buttonNegative: "Cancel",
    buttonPositive: "OK"
  
);

if (granted === PermissionsAndroid.RESULTS.GRANTED) 
  console.log("You can use the Microphone");
 else 
  console.log("Microphone permission denied");
  

micPermission = async () => 
    let granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
      
        title: "Audio Permission",
        message:
          "App needs access to your audio / microphone",
        buttonNeutral: "Ask Me Later",
        buttonNegative: "Cancel",
        buttonPositive: "OK"
      
    );

    if (granted === PermissionsAndroid.RESULTS.GRANTED) 
      console.log("You can use the Microphone");
     else 
      console.log("Microphone permission denied");
        

第四步:触发componentDidMount的权限

componentDidMount() 
  this.cameraPermission();
  this.micPermission();

第 5 步:添加带有媒体属性的 WebView

constructor(props) 
    super(props);
    this.state = 
      url: 'https://yoururl.com',
      userAgent: 'Mozilla/5.0 (Linux; An33qdroid 10; Android SDK built for x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.185 Mobile Safari/537.36'
    
  
render() 
return (
  <WebView
    userAgent=this.state.userAgent //Set your useragent (Browser) **Very Important
    originWhitelist=['*']
    allowsInlineMediaPlayback
    bounces=true
    source=
      uri: this.state.url, //URL of your redirect site
    
    startInLoadingState
    scalesPageToFit
    javaScriptEnabled=true
  />
);

希望对您有所帮助!

【讨论】:

this.state.userAgent 设置为什么? 我试过这个,但仍然面临同样的问题。如何获取 userAgent? @DarylAranha 和@ilia-sidorenko,我已经更新了答案。 userAgent: 'Mozilla/5.0 (Linux; An33qdroid 10; Android SDK built for x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.185 Mobile Safari/537.36' @AdheepMohamedAbdulKader 这段代码仍然适用于您吗?因为我面临与 WebView ***.com/questions/69812167/… 相同的问题 @DarylAranha 你在你的项目中解决了这个问题吗?【参考方案3】:

我认为 onPermissionRequest() 方法在最新版本的 react native webview 包中重复。我刚刚评论了第二个解决了我的问题。

您应该评论RNCWebViewManager.java 文件。可以从以下路径获取node_modules\react-native-webview\android\src\main\java\com\reactnativecommunity\webview

【讨论】:

我在更新的 webview 包中找不到任何函数名称onPermissionRequest()。能否请您发送整个代码,也许它丢失了这就是我面临同样问题的原因?

以上是关于React Native:Webview getUserMedia 不起作用(onPermissionRequest 覆盖?)的主要内容,如果未能解决你的问题,请参考以下文章

React Native WebView 状态栏

React Native开发React Native控件之WebView组件详解以及实例使用(22)

React-native:如何自动保存 webview 当前网页?

WebView 不一致地显示白屏 react-native

有啥方法可以在 react-native-webview 中禁用 hapticFeedback

如何在 iOS 的 WebView(react-native-webview) 中设置自定义字体?