QuickStart: Add video calling to your WebView client app

Webview technology is an embeddable browser that can be integrated directly into a native mobile application. If you want to develop an Azure Communication Services calling application directly a native Android application, besides using the Azure Communication Calling Android SDK, you can also use Azure Communication Calling Web SDK on Android WebView. In this quickstart, you'll learn how to run webapps developed with the Azure Communication Calling Web SDK in an Android WebView environment.

Prerequisites

Important

Functionality described in this article is currently in public preview. This preview version is provided without a service-level agreement, and we don't recommend it for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see Supplemental Terms of Use for Microsoft Azure Previews.

This quickstart guide assumes that you already have an Android WebView application. If you don't have one, you can download the WebViewQuickstart sample app.

If you use the WebViewQuickstart sample app, all the necessary configurations and permission handling are in place. You can skip to Known Issues. All you have to do is update the defaultUrl in MainActivity to the url of the calling web application you deployed and build the application.

Add permissions to application manifest

To request the permissions required to make a call, you must declare the permissions in the application manifest. (app/src/main/AndroidManifest.xml) Make sure you have the following permissions added to the application manifest:

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

Request permissions at run time

Adding permissions to the application manifest isn't enough. You must also request the dangerous permissions at runtime to access camera and microphone.

You have to call requestPermissions() and override onRequestPermissionsResult. Besides app permissions, you have to handle browser permission requests by overriding WebChromeClient.onPermissionRequest

The WebViewQuickstart sample app also shows how to handle permission requests from browser and request app permissions at runtime.

WebView configuration

Azure Communication Calling Web SDK requires JavaScript enabled. In some cases, we saw play() can only be initiated by a user gesture error message in Android WebView environment, and users aren't able to hear incoming audio. Therefore, we recommend setting MediaPlaybackRequiresUserGesture to false.

WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setMediaPlaybackRequiresUserGesture(false);

Known issues

MediaDevices.enumerateDevices() API returns empty labels

It's a known issue on Android WebView. The issue will affect the following API in Web SDK:

  • DeviceManager.getCameras()

  • DeviceManager.getMicrophones()

  • DeviceManager.getSpeakers() (If the device supports speaker enumeration)

    The value of name field in the result object will be an empty string. This issue won't affect the function of streaming in the video call but the application users won't be able to know the camera label they select for sending the video. To provide a better UI experience, you can use the following workaround in the web application to get device labels and map the label by deviceId.

    Although we can't get device labels from MediaDevices.enumerateDevices(), we can get the label from MediaStreamTrack. This workaround requires the web application to use getUserMedia to get the stream and map the deviceId. If there are many cameras and microphones on the Android device, it may take a while to collect labels.

async function getDeviceLabels() {
    const devices = await navigator.mediaDevices.enumerateDevices();
    const result = {};
    for (let i = 0; i < devices.length; i++) {
        const device = devices[i];
        if(device.kind != 'audioinput' && device.kind != 'videoinput') continue;
        if(device.label) continue;
        const deviceId = device.deviceId;
        const deviceConstraint = {
            deviceId: {
                exact: deviceId
            }
        };		
        const constraint = (device.kind == 'audioinput') ?
                { audio: deviceConstraint } :
                { video: deviceConstraint };
		try {
            const stream =  await navigator.mediaDevices.getUserMedia(constraint);
            stream.getTracks().forEach(track => {
                let namePrefix = '';
                if (device.kind == 'audioinput') {
                    namePrefix = 'microphone:';
                } else if(device.kind == 'videoinput') {
                    namePrefix = 'camera:';
                }
                if (track.label === '' && deviceId == 'default') {
                    result[namePrefix + deviceId] = 'Default';
                } else {
                    result[namePrefix + deviceId] = track.label;
                }
                track.stop();
            });
		} catch(e) {
		    console.error(`get stream failed: ${device.kind} ${deviceId}`, e);
		}
    }
    return result;
}

Screenshot of getDeviceLabels() result.

After you get the mapping between deviceId and label, you can use it to relate the label with the id from DeviceManager.getCameras() or DeviceManager.getMicrophones()

Screenshot showing the device name workaround.

iOS WKWebView allows you to embed web content seamlessly into your app UI. If you want to develop an Azure Communication Services calling application on iOS, besides using the Azure Communication Calling iOS SDK, you can also use Azure Communication Calling Web SDK with iOS WKWebView. In this quickstart, you'll learn how to run webapps developed with the Azure Communication Calling Web SDK in an iOS WKWebView environment.

Prerequisites

Important

Functionality described in this article is currently in public preview. This preview version is provided without a service-level agreement, and we don't recommend it for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see Supplemental Terms of Use for Microsoft Azure Previews.

This quickstart guide assumes that you're familiar with iOS application development. We'll mention the necessary configuration and tips when developing iOS WKWebView application for Azure Communication Services Calling SDK.

Add keys in Info.plist

To make a video call, make sure you have the following keys added to the Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NSMicrophoneUsageDescription</key>
	<string>Camera access is required to make a video call</string>
	<key>NSCameraUsageDescription</key>
	<string>Microphone access is required to make an audio call</string>
</dict>
</plist>

Handle permission prompt

On iOS Safari, users can see permission prompt more frequent than on other platforms. It is because Safari doesn't keep permissions for a long time unless there's a stream acquired.

WKWebView provides a way to handle browser permission prompt by using WKUIDelegate.webView. This API is only available on iOS 15.0+.

Here's an example. In this example, the browser permission is granted in decisionHandler, so users won't see browser permission prompt after they grant the app permissions.

import WebKit
import SwiftUI

struct WebView: UIViewRepresentable {
    private(set) var url: URL
    private var webView: WKWebView

    init(url: String) {
        self.url = URL(string: url)!

        let prefs = WKWebpagePreferences()
        prefs.allowsContentJavaScript = true
        prefs.preferredContentMode = .recommended

        let configuration = WKWebViewConfiguration()
        configuration.defaultWebpagePreferences = prefs
        configuration.allowsInlineMediaPlayback = true
        configuration.mediaTypesRequiringUserActionForPlayback = []
        let webView = WKWebView(frame: CGRect(), configuration: configuration)
        self.webView = webView
    }

    class Coordinator: NSObject, WKUIDelegate {
        let parent: WebView
        init(_ parent: WebView) {
            self.parent = parent
        }
        func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
            decisionHandler(WKPermissionDecision.grant)
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView {
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.uiDelegate = context.coordinator
        uiView.load(URLRequest(url: self.url))
    }
}

WebView configuration

Azure Communication Calling Web SDK requires JavaScript enabled.

allowsInlineMediaPlayback is also required to be true.

let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = true
prefs.preferredContentMode = .recommended

let configuration = WKWebViewConfiguration()
configuration.defaultWebpagePreferences = prefs
configuration.allowsInlineMediaPlayback = true
let webView = WKWebView(frame: CGRect(), configuration: configuration)

Known issues

Microphone is muted when app goes to background

When a user locks the screen or WkWebView app goes to background, the microphone input will be muted until the app comes back to foreground. This is iOS WkWebView system behavior, and the microphone isn't muted by Azure Communication Services Calling Web SDK.

Connection drops soon after the app goes to background

This is also iOS app behavior. When we switch to other audio/video app, the connection will drop around 30 seconds later. This isn't a problem if the app only stays in background for a short time. When the app comes back to foreground, the call will recover. If the app stays in background for a longer period, the server will think the user is away and remove the user from the participants list. In this case, when the user switches the WkWebView app back to foreground, the call will disconnect and won't recover.

Next steps

For more information, see the following articles: