LogoLogo
WherebyStatusCommunity
  • 📹Whereby 101
    • Create Your Video Experience
      • Get started in 3 steps
      • Embedding Whereby in a web app
        • Using Whereby's Web Component & Pre-built UI
          • Script Tags
          • With Low Code
            • Embedding in Squarespace or Wordpress
            • No code video conferencing in Bubble
        • Using Whereby's Browser SDK with React Hooks for a fully custom UI
      • Embedding Whereby in a mobile app
        • Embedding Whereby in iOS
          • Using Whereby's Native iOS SDK
        • Embedding Whereby in Android
          • Using Whereby's Native SDK
        • Using Flutter
        • Using React Native
      • Github SDK Examples
      • Meeting scheduling with Cronofy
    • Generating Room URLs
      • Name prefixes
      • Using “Create a room”
      • Using Postman
    • Customize Your Video Experience
      • During room creation
      • Using Attributes/URL Params
      • Global preferences
      • Branding elements
      • Dial-In
      • File sharing
      • Breakout Groups with Embedded
      • Waiting Rooms
    • User roles & Meeting Permissions
    • FAQ
      • Accessibility
      • Whereby Words
      • Firewall & Security
      • HIPAA compliant setup
      • Allowed Domains & Localhost
      • Whereby Embedded Feature Comparison
  • 🔍Meeting Content & Quality
    • Recording
      • Cloud Recording
      • Local Recording
    • Transcribing
      • Session Transcription
      • Recording Transcription
    • Live Captions
    • Session summaries
    • Live streaming RTMP
    • Quality Insights
      • Real-time troubleshooting
      • Using the Insights dashboard
      • Improving call quality
      • Tracking room events with Webhooks
  • 🤷End User
    • End User Support Guides
      • Supported Browsers & Devices
      • Screen Sharing Setup & Usage
      • Using Breakout Groups
      • Troubleshooting & Basics
  • 🚚Developer Guides
    • Quickly deploy Whereby to your domain
    • Tracking Customer Usage
    • Migrating from Twilio
      • Twilio JS SDK Quick Migration
      • Twilio JS SDK Direct Migration
  • 🖥️Reference
    • REST API Reference
      • /meetings
      • /insights
      • /recordings
      • /transcriptions
      • /summaries
      • /rooms
    • Web Component Reference
    • React Hooks Reference
      • Quick Start
        • Getting started with the Browser SDK
      • Guides & Tutorials
        • Migrate from version 2.x to 3
        • Grid logic
        • Custom Video Tiles with React
        • Usage with Next.js
        • How to customize the toolbar
      • API Reference
        • WherebyProvider
        • VideoView
        • VideoGrid
        • useLocalMedia
        • useRoomConnection
      • Types
    • React Native Reference
      • Quick Start
      • WherebyEmbed
    • Webhooks Reference
Powered by GitBook
On this page

Was this helpful?

Edit on GitHub
  1. Whereby 101
  2. Create Your Video Experience
  3. Embedding Whereby in a mobile app

Embedding Whereby in Android

Learn how to embed Whereby rooms in your native Android app using the WebView class.

Last updated 2 months ago

Was this helpful?

We offer native SDKs that allow you to tap into powerful features such as listening to room events and use custom buttons to send commands to the room from your application.

In order to embed a room in Android, add these permissions to the manifest:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<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" />

You will need to use the class. Start by creating a custom WebChromeClient class and override the onPermissionRequest method:

import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;

public class CustomWebChromeClient extends WebChromeClient {
    private Activity activity;

    public CustomWebChromeClient(Activity parentActivity) {
        super();
        activity = parentActivity;
    }

    @Override
    public void onPermissionRequest(final PermissionRequest request) {
        activity.runOnUiThread(new Runnable() {
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void run() {
                request.grant(request.getResources());
            }
        });
    }
}
import android.app.Activity
import android.webkit.PermissionRequest
import android.webkit.WebChromeClient

class CustomWebChromeClient(private val activity: Activity) : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        activity.runOnUiThread { request.grant(request.resources) }
    }
}

Then, create a WebViewUtils helper class to configure the WebView. Here is a possible configuration:

import android.annotation.SuppressLint;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class WebViewUtils {

    @SuppressLint("SetJavaScriptEnabled")
    public static void configureWebView(WebView webView) {
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setMediaPlaybackRequiresUserGesture(false);
        CookieManager.getInstance().setAcceptCookie(true);
        CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
    }
}
import android.annotation.SuppressLint
import android.webkit.CookieManager
import android.webkit.WebView

object WebViewUtils {
    @SuppressLint("SetJavaScriptEnabled")
    fun configureWebView(webView: WebView) {
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        webSettings.databaseEnabled = true
        webSettings.mediaPlaybackRequiresUserGesture = false
        CookieManager.getInstance().setAcceptCookie(true)
        CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
    }
}

Finally, set up your activity following these steps:

  1. Configure the webView.

  2. Request the permissions if needed.

  3. Add the ?skipMediaPermissionPrompt parameter to the room URL and load it.

Here is an example:

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    public String roomUrlString = ""; // Replace by your own
    private String roomParameters = "?skipMediaPermissionPrompt";

    private static final int PERMISSION_REQUEST_CODE = 1234;
    private String[] requiredDangerousPermissions = {
            Manifest.permission.CAMERA,
            Manifest.permission.MODIFY_AUDIO_SETTINGS,
            Manifest.permission.RECORD_AUDIO
    };

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.webView = findViewById(R.id.webView);
        WebViewUtils.configureWebView(this.webView);
        this.webView.setWebChromeClient(new CustomWebChromeClient(this));
        this.webView.setWebViewClient(new WebViewClient());
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (this.webView.getUrl() == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && this.isPendingPermissions()) {
                // This explicitly requests the camera and audio permissions.
                // It's fine for a demo app but should probably be called earlier in the flow,
                // on a user interaction instead of onResume.
                this.requestCameraAndAudioPermissions();
            } else {
                this.loadEmbeddedRoomUrl();
            }
        }
    }

    private void loadEmbeddedRoomUrl() {
        this.webView.loadUrl(roomUrlString + roomParameters);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE:
                if (this.grantResultsContainsDenials(grantResults)) {
                    // Show some permissions required dialog.
                } else {
                    // All necessary permissions granted, continue loading.
                    this.loadEmbeddedRoomUrl();
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private void requestCameraAndAudioPermissions() {
        this.requestPermissions(this.getPendingPermissions(), PERMISSION_REQUEST_CODE);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private String[] getPendingPermissions() {
        List<String> pendingPermissions = new ArrayList<>();
        for (String permission : this.requiredDangerousPermissions) {
            if (this.checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
                pendingPermissions.add(permission);
            }
        }
        return pendingPermissions.toArray(new String[pendingPermissions.size()]);
    }

    private boolean isPendingPermissions() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return false;
        }
        return this.getPendingPermissions().length > 0;
    }

    private boolean grantResultsContainsDenials(int[] grantResults) {
        for (int result : grantResults) {
            if (result == PackageManager.PERMISSION_DENIED) {
                return true;
            }
        }
        return false;
    }
}
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    var roomUrlString = "" // Replace by your own
    private val roomParameters = "?skipMediaPermissionPrompt"

    companion object {
        private const val PERMISSION_REQUEST_CODE = 1234
    }

    private val requiredDangerousPermissions = arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.MODIFY_AUDIO_SETTINGS,
        Manifest.permission.RECORD_AUDIO
    )

    private var webView: WebView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        webView = findViewById(R.id.webView)
        WebViewUtils.configureWebView(webView!!)
        webView!!.setWebChromeClient(CustomWebChromeClient(this))
        webView!!.setWebViewClient(WebViewClient())
    }

    override fun onResume() {
        super.onResume()
        if (webView!!.url == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPendingPermissions()) {
                // This explicitly requests the camera and audio permissions.
                // It's fine for a demo app but should probably be called earlier in the flow,
                // on a user interaction instead of onResume.
                requestCameraAndAudioPermissions()
            } else {
                loadEmbeddedRoomUrl()
            }
        }
    }

    private fun loadEmbeddedRoomUrl() {
        webView!!.loadUrl(roomUrlString + roomParameters)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        when (requestCode) {
            PERMISSION_REQUEST_CODE -> if (grantResultsContainsDenials(grantResults)) {
                // Show some permissions required dialog.
            } else {
                // All necessary permissions granted, continue loading.
                loadEmbeddedRoomUrl()
            }
            else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private fun requestCameraAndAudioPermissions() {
        requestPermissions(pendingPermissions, PERMISSION_REQUEST_CODE)
    }

    @get:RequiresApi(api = Build.VERSION_CODES.M)
    private val pendingPermissions: Array<String>
        private get() {
            val pendingPermissions: MutableList<String> = ArrayList()
            for (permission in requiredDangerousPermissions) {
                if (checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
                    pendingPermissions.add(permission)
                }
            }
            return pendingPermissions.toTypedArray()
        }

    private fun isPendingPermissions(): Boolean {
        return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            false
        } else pendingPermissions.isNotEmpty()
    }

    private fun grantResultsContainsDenials(grantResults: IntArray): Boolean {
        for (result in grantResults) {
            if (result == PackageManager.PERMISSION_DENIED) {
                return true
            }
        }
        return false
    }

}

📹
Read more
WebView