React Native:Webview getUserMedia 不工作(onPermissionRequest 覆盖?)
React Native: Webview getUserMedia not working (onPermissionRequest override?)
我正在使用 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?如果可能,我必须编辑哪些文件?是否有任何第三方模块(最新的)实现了这个(还没有找到)?
谢谢。
最后我不得不在原生 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()
);
}
请记住,listproject
、permissionwebview
等名称可以更改为您需要的任何名称,只要您在文件的名称空间和包引用。
一旦你拥有所有这些,你只需创建 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.android.js
,因为此组件是为 Android 制作的,因此它仅包含在 Android 平台中。
有了这个,一切都应该工作,WebView 组件在使用时应该请求许可。
编辑:
我已将代码上传到我的 Github repository。我只是更改了一些名称,但核心代码并没有改变一点以确保它仍然有效(我无法测试它,所以最好不要更改很多东西)。
希望对大家有所帮助!
在我的例子中,以下代码有效。
第 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");
}
}
第 4 步:触发 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}
/>
);
}
希望对您有所帮助!
我认为 onPermissionRequest() 方法在最新版本的 react native webview 包中重复了。我刚刚评论说第二个解决了我的问题。
您应该对 RNCWebViewManager.java
文件发表评论。您可以从以下路径获取它 node_modules\react-native-webview\android\src\main\java\com\reactnativecommunity\webview
我正在使用 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?如果可能,我必须编辑哪些文件?是否有任何第三方模块(最新的)实现了这个(还没有找到)?
谢谢。
最后我不得不在原生 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()
);
}
请记住,listproject
、permissionwebview
等名称可以更改为您需要的任何名称,只要您在文件的名称空间和包引用。
一旦你拥有所有这些,你只需创建 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.android.js
,因为此组件是为 Android 制作的,因此它仅包含在 Android 平台中。
有了这个,一切都应该工作,WebView 组件在使用时应该请求许可。
编辑:
我已将代码上传到我的 Github repository。我只是更改了一些名称,但核心代码并没有改变一点以确保它仍然有效(我无法测试它,所以最好不要更改很多东西)。
希望对大家有所帮助!
在我的例子中,以下代码有效。
第 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");
}
}
第 4 步:触发 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}
/>
);
}
希望对您有所帮助!
我认为 onPermissionRequest() 方法在最新版本的 react native webview 包中重复了。我刚刚评论说第二个解决了我的问题。
您应该对 RNCWebViewManager.java
文件发表评论。您可以从以下路径获取它 node_modules\react-native-webview\android\src\main\java\com\reactnativecommunity\webview