Search…
In Android apps
Learn how to embed Whereby rooms in your native Android app using the WebView class.
In order to embed a room in Android, add these permissions to the manifest:
XML
1
<uses-permission android:name="android.permission.CAMERA" />
2
<uses-permission android:name="android.permission.INTERNET" />
3
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
4
<uses-permission android:name="android.permission.RECORD_AUDIO" />
5
<uses-permission android:name="android.permission.WAKE_LOCK" />
Copied!
You will need to use the WebView class. Start by creating a custom WebChromeClient class and override the onPermissionRequest method:
Java
Kotlin
1
import android.annotation.TargetApi;
2
import android.app.Activity;
3
import android.os.Build;
4
import android.webkit.PermissionRequest;
5
import android.webkit.WebChromeClient;
6
7
public class CustomWebChromeClient extends WebChromeClient {
8
private Activity activity;
9
10
public CustomWebChromeClient(Activity parentActivity) {
11
super();
12
activity = parentActivity;
13
}
14
15
@Override
16
public void onPermissionRequest(final PermissionRequest request) {
17
activity.runOnUiThread(new Runnable() {
18
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
19
@Override
20
public void run() {
21
request.grant(request.getResources());
22
}
23
});
24
}
25
}
Copied!
1
import android.app.Activity
2
import android.webkit.PermissionRequest
3
import android.webkit.WebChromeClient
4
5
class CustomWebChromeClient(private val activity: Activity) : WebChromeClient() {
6
override fun onPermissionRequest(request: PermissionRequest) {
7
activity.runOnUiThread { request.grant(request.resources) }
8
}
9
}
Copied!
Then, create a WebViewUtils helper class to configure the WebView. Here is a possible configuration:
Java
Kotlin
1
import android.annotation.SuppressLint;
2
import android.webkit.CookieManager;
3
import android.webkit.WebSettings;
4
import android.webkit.WebView;
5
6
public class WebViewUtils {
7
8
@SuppressLint("SetJavaScriptEnabled")
9
public static void configureWebView(WebView webView) {
10
WebSettings webSettings = webView.getSettings();
11
webSettings.setJavaScriptEnabled(true);
12
webSettings.setDomStorageEnabled(true);
13
webSettings.setDatabaseEnabled(true);
14
webSettings.setMediaPlaybackRequiresUserGesture(false);
15
CookieManager.getInstance().setAcceptCookie(true);
16
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
17
}
18
}
Copied!
1
import android.annotation.SuppressLint
2
import android.webkit.CookieManager
3
import android.webkit.WebView
4
5
object WebViewUtils {
6
@SuppressLint("SetJavaScriptEnabled")
7
fun configureWebView(webView: WebView) {
8
val webSettings = webView.settings
9
webSettings.javaScriptEnabled = true
10
webSettings.domStorageEnabled = true
11
webSettings.databaseEnabled = true
12
webSettings.mediaPlaybackRequiresUserGesture = false
13
CookieManager.getInstance().setAcceptCookie(true)
14
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
15
}
16
}
Copied!
Finally, set up your activity following these steps:
  1. 1.
    Configure the webView.
  2. 2.
    Request the permissions if needed.
  3. 3.
    Add the ?skipMediaPermissionPrompt parameter to the room URL and load it.
Here is an example:
Java
Kotlin
1
import androidx.annotation.NonNull;
2
import androidx.annotation.RequiresApi;
3
import androidx.appcompat.app.AppCompatActivity;
4
import android.Manifest;
5
import android.content.pm.PackageManager;
6
import android.os.Build;
7
import android.os.Bundle;
8
import android.webkit.WebView;
9
import android.webkit.WebViewClient;
10
11
import java.util.ArrayList;
12
import java.util.List;
13
14
public class MainActivity extends AppCompatActivity {
15
16
public String roomUrlString = ""; // Replace by your own
17
private String roomParameters = "?skipMediaPermissionPrompt";
18
19
private static final int PERMISSION_REQUEST_CODE = 1234;
20
private String[] requiredDangerousPermissions = {
21
Manifest.permission.CAMERA,
22
Manifest.permission.MODIFY_AUDIO_SETTINGS,
23
Manifest.permission.RECORD_AUDIO
24
};
25
26
private WebView webView;
27
28
@Override
29
protected void onCreate(Bundle savedInstanceState) {
30
super.onCreate(savedInstanceState);
31
setContentView(R.layout.activity_main);
32
this.webView = findViewById(R.id.webView);
33
WebViewUtils.configureWebView(this.webView);
34
this.webView.setWebChromeClient(new CustomWebChromeClient(this));
35
this.webView.setWebViewClient(new WebViewClient());
36
}
37
38
@Override
39
protected void onResume() {
40
super.onResume();
41
if (this.webView.getUrl() == null) {
42
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && this.isPendingPermissions()) {
43
// This explicitly requests the camera and audio permissions.
44
// It's fine for a demo app but should probably be called earlier in the flow,
45
// on a user interaction instead of onResume.
46
this.requestCameraAndAudioPermissions();
47
} else {
48
this.loadEmbeddedRoomUrl();
49
}
50
}
51
}
52
53
private void loadEmbeddedRoomUrl() {
54
this.webView.loadUrl(roomUrlString + roomParameters);
55
}
56
57
@Override
58
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
59
switch (requestCode) {
60
case PERMISSION_REQUEST_CODE:
61
if (this.grantResultsContainsDenials(grantResults)) {
62
// Show some permissions required dialog.
63
} else {
64
// All necessary permissions granted, continue loading.
65
this.loadEmbeddedRoomUrl();
66
}
67
break;
68
default:
69
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
70
}
71
}
72
73
@RequiresApi(api = Build.VERSION_CODES.M)
74
private void requestCameraAndAudioPermissions() {
75
this.requestPermissions(this.getPendingPermissions(), PERMISSION_REQUEST_CODE);
76
}
77
78
@RequiresApi(api = Build.VERSION_CODES.M)
79
private String[] getPendingPermissions() {
80
List<String> pendingPermissions = new ArrayList<>();
81
for (String permission : this.requiredDangerousPermissions) {
82
if (this.checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
83
pendingPermissions.add(permission);
84
}
85
}
86
return pendingPermissions.toArray(new String[pendingPermissions.size()]);
87
}
88
89
private boolean isPendingPermissions() {
90
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
91
return false;
92
}
93
return this.getPendingPermissions().length > 0;
94
}
95
96
private boolean grantResultsContainsDenials(int[] grantResults) {
97
for (int result : grantResults) {
98
if (result == PackageManager.PERMISSION_DENIED) {
99
return true;
100
}
101
}
102
return false;
103
}
104
}
Copied!
1
import android.Manifest
2
import android.content.pm.PackageManager
3
import android.os.Build
4
import android.os.Bundle
5
import android.webkit.WebView
6
import android.webkit.WebViewClient
7
import androidx.annotation.RequiresApi
8
import androidx.appcompat.app.AppCompatActivity
9
10
class MainActivity : AppCompatActivity() {
11
12
var roomUrlString = "" // Replace by your own
13
private val roomParameters = "?skipMediaPermissionPrompt"
14
15
companion object {
16
private const val PERMISSION_REQUEST_CODE = 1234
17
}
18
19
private val requiredDangerousPermissions = arrayOf(
20
Manifest.permission.CAMERA,
21
Manifest.permission.MODIFY_AUDIO_SETTINGS,
22
Manifest.permission.RECORD_AUDIO
23
)
24
25
private var webView: WebView? = null
26
27
override fun onCreate(savedInstanceState: Bundle?) {
28
super.onCreate(savedInstanceState)
29
setContentView(R.layout.activity_main)
30
webView = findViewById(R.id.webView)
31
WebViewUtils.configureWebView(webView!!)
32
webView!!.setWebChromeClient(CustomWebChromeClient(this))
33
webView!!.setWebViewClient(WebViewClient())
34
}
35
36
override fun onResume() {
37
super.onResume()
38
if (webView!!.url == null) {
39
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPendingPermissions()) {
40
// This explicitly requests the camera and audio permissions.
41
// It's fine for a demo app but should probably be called earlier in the flow,
42
// on a user interaction instead of onResume.
43
requestCameraAndAudioPermissions()
44
} else {
45
loadEmbeddedRoomUrl()
46
}
47
}
48
}
49
50
private fun loadEmbeddedRoomUrl() {
51
webView!!.loadUrl(roomUrlString + roomParameters)
52
}
53
54
override fun onRequestPermissionsResult(
55
requestCode: Int,
56
permissions: Array<String>,
57
grantResults: IntArray
58
) {
59
when (requestCode) {
60
PERMISSION_REQUEST_CODE -> if (grantResultsContainsDenials(grantResults)) {
61
// Show some permissions required dialog.
62
} else {
63
// All necessary permissions granted, continue loading.
64
loadEmbeddedRoomUrl()
65
}
66
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
67
}
68
}
69
70
@RequiresApi(api = Build.VERSION_CODES.M)
71
private fun requestCameraAndAudioPermissions() {
72
requestPermissions(pendingPermissions, PERMISSION_REQUEST_CODE)
73
}
74
75
@get:RequiresApi(api = Build.VERSION_CODES.M)
76
private val pendingPermissions: Array<String>
77
private get() {
78
val pendingPermissions: MutableList<String> = ArrayList()
79
for (permission in requiredDangerousPermissions) {
80
if (checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
81
pendingPermissions.add(permission)
82
}
83
}
84
return pendingPermissions.toTypedArray()
85
}
86
87
private fun isPendingPermissions(): Boolean {
88
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
89
false
90
} else pendingPermissions.isNotEmpty()
91
}
92
93
private fun grantResultsContainsDenials(grantResults: IntArray): Boolean {
94
for (result in grantResults) {
95
if (result == PackageManager.PERMISSION_DENIED) {
96
return true
97
}
98
}
99
return false
100
}
101
102
}
Copied!
Copy link