Android

Android integration

When integrating a WebView with WebRTC into your native application, there are several key elements and permissions that need to be declared in the AndroidManifest.xml file.

Permissions

Permissions are declared in the manifest file using the <uses-permission> element. For a WebView with WebRTC, you typically need to declare the following permissions:

  • INTERNET: Allows your application to open network sockets. This is necessary for the WebView to load web content.

<uses-permission android:name="android.permission.INTERNET" />
  • CAMERA: Allows your application to use local device camera. This is required for WebRTC pages that capture images or video.

<uses-permission android:name="android.permission.CAMERA" />
  • RECORD_AUDIO: Allows your application to use local device audio input. This is required for WebRTC pages that capture audio.

<uses-permission android:name="android.permission.RECORD_AUDIO" />
  • MODIFY_AUDIO_SETTINGS: It's needed for WebRTC to control the audio settings for optimal communication. Pay additional attention, that only RECORD_AUDIO permissions are not enough to let application properly using audio channels

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Features

This element is used to declare hardware or software features used by the application, which can affect whether or not the application can be installed on a device. For a WebView with WebRTC, you typically need to declare the following feature:

  • android.hardware.camera: Indicates that the application uses the device's camera

<uses-feature android:name="android.hardware.camera" android:required="true" />
  • android.hardware.audio: Indicates that the application uses the device's microphone

<uses-feature android:name="android.hardware.audio" android:required="true" />

If you are only going to have a viewer experience, then there is no need to require either camera or audio features

Layout

Locate WebView in your activity layout. In this case, it will be activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Activities

For a WebView with WebRTC, you need to declare the activity that hosts the WebView:

<activity android:name=".MainActivity" android:exported="true">
</activity>

Replace MainActivity with the name of your activity that hosts the WebView.

Make changes in AndroidManifest.xml file with the necessary permissions and features for a WebView with WebRTC:

.....
    <uses-feature android:name="android.hardware.camera"
        android:required="true" />
        
    <uses-feature android:name="android.hardware.audio"
        android:required="true" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

    <application       
       .......
        android:theme="@style/Theme.YourAppTheme"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

Replace YourAppTheme and MainActivity with the theme and activity names used in your application.

In the MainActivity check for camera and record audio permissions first. The REQUEST_CAMERA_MIC_PERMISSION constant is used as a request code for permission requests. If permissions are not granted then WebView will not work as expected.

Permission Request

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
            || ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_MIC_PERMISSION);
    }
}

In the onCreate method, the app checks if it has the necessary permissions (camera and microphone). If not, it requests these permissions.

Handling Permission Request Result

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_CAMERA_MIC_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
            // Permissions granted
            // init() // initiate WebView and load url
        } else {
            // Permissions denied
        }
    }
}

The onRequestPermissionsResult method handles the result of the permission request. If the permissions are granted, the app can proceed with accessing the camera and microphone. If not, the app should handle the denial appropriately.

WebView Configuration

@SuppressLint("SetJavaScriptEnabled")
...
    WebView webView = view.findViewById(R.id.web_view);
    WebSettings webSettings = webView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webSettings.setDomStorageEnabled(true);
    webSettings.setMediaPlaybackRequiresUserGesture(false);

    webView.setWebChromeClient(new WebChromeClient() {
        @Override
        public void onPermissionRequest(final PermissionRequest request) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    request.grant(request.getResources());
                }
            });
        }
    });
    webView.loadUrl("https://celebrity-demo.ui.sceenic.co/subscriber");

In the onCreate method, the WebView is configured. JavaScript is enabled, DOM storage is enabled, and media playback does not require user gestures. A WebChromeClient is set to handle permission requests within the WebView. Finally, the WebView loads a specific URL.

MainActivity

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_CAMERA_MIC_PERMISSION = 123;
    private WebView webView = null;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      
        // Check if the permissions are granted
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            // If not, request the permissions
            ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_MIC_PERMISSION);
        } else {
            // If granted
            init() // initiate WebView and load url
        }
    }
  
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == REQUEST_CAMERA_MIC_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                // Permissions granted initiate WebView and load url
                init()
            } else {
                // Permissions denied
                finish() // close the application
            }
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Clear WebView
        if(webView != null) {
            webView.loadUrl("about:blank")
            webView = null
        }
    }    

    @SuppressLint("SetJavaScriptEnabled")    
    private void init() {
        webView = findViewById(R.id.web_view);
        WebSettings webSettings = webView.getSettings();

        // Enable JavaScript
        webSettings.setJavaScriptEnabled(true);

        // Enable DOM storage API
        webSettings.setDomStorageEnabled(true);

        // Allow video autoplay
        webSettings.setMediaPlaybackRequiresUserGesture(false);

        // Set a WebChromeClient that handles permission requests
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        request.grant(request.getResources());
                    }
                });
            }
        });

        webView.loadUrl("https://celebrity-demo.ui.sceenic.co/subscriber");    
    }
}

Last updated