React Native: Webview getUserMedia not working (onPermissionRequest override?)

I think onPermissionRequest() method is duplicated in the latest version of react native webview package. I just commented the second one resolved my issue.

You should comment on the RNCWebViewManager.java file. You can get it from the following path node_modules\react-native-webview\android\src\main\java\com\reactnativecommunity\webview

enter image description here

enter image description here


In my case the following code works.

Step 1: Add permissions to 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 -->

Step 2: Add import statement for 'PermissionsAndroid' from 'react-native' package

import {
  PermissionsAndroid
} from 'react-native';

Step 3: Write async methods to acquire required permission, here I am getting Camera and Microphone permissions

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");
    }    
}

Step 4: Trigger the permission on componentDidMount

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

Step 5: Add WebView with media attributes

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}
  />
);
}

Hope this is helpful!


Finally I had to implement an own WebView component in native Android. The problem is the onPermissionRequest() function that it's not implemented in the native WebView. You have to create your own and override that function. In my case, I had to add the following code (all files are under path [react-native-project]/android/app/src/main/java/com/listproject/permissionwebview/):

In 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();
    }
}

In 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());
            }
        });
    }
}

In 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);
    }
}

Finally, update your MainApplication.java file, and add your package into getPackages() function:

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

Keep in mind that the names of listproject, permissionwebview, and so on can be changed to whatever you need to, as long as you change them in the namespaces of the files and in the package references.

Once you have all this, all you have to create is the ReactNative component export ([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);

Note we set the name of index to index.android.js because this component is made for Android, so it's included only in Android platform.

With this, everything should work and the WebView component should ask for permission when it's used.

EDIT:

I've uploaded the code to my Github repository. I've only changed some names, but the core code has not changed a bit to make sure it still works (I cannot test it, so better not to change lots of things).

Hope it helps someone!