This document explains how you can implement the Intercom with the iFrame jitsi API. You must implement the GraphQL calls in order for the feature to work properly with your infrastructure and our backend. Regarding the user experience, some of the documentation might be optional.
Parsing Intercom Push messages only works with Android Api Level 26+ (Android 8+). The reason is that we are encrypting the credentials that are sent to the mobile using AES_256/CBC/PKCS5Padding which is only available on Android 26+. If you use a lower Android version deserialzing of Intercom push messages will fail silently.
Please read Push Notification to understand how to receive push messages from the Sensorberg backend.
The SDK sends two type of push messages for the Intercom:
The follwoing is just a suggestion of how to implement Intercom Audio & Video. We are not giving any support beyond this implementation.
The Video can be displayed in a WebView.
The Call.Incoming PushMessage
has a Video property that contains a url
which you can pass to the WebView.
When the web page with the video gets loaded you need to authenticate with username
and password
:
webView.webViewClient = object : WebViewClient() {
override fun onReceivedHttpAuthRequest(view: WebView?, handler: HttpAuthHandler?, host: String?, realm: String?) {
handler?.proceed(video.username, video.password)
}
}
webView.loadUrl(video.url)
For the audio, you can either load it into a WebView as well, or you could decide going with a Jitsi SDK. The following explains how to do for a WebView.
The [Call.Incoming] PushMessage
has a Audio property, containing roomName
, domain
and jsonWebToken
.
To configure Jitsi from the WebView you can use JavaScript API with @JavascriptInterface
.
First you create a jitsi.html
in the assets
directory:
<!-- This is a static HTML page. This way we can use the Jitsi JavaScript Api. -->
<!-- See: https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Intercom</title>
<meta name="viewport" content="width=device-width,height=device-height,maximum-scale=1.0,minimum-scale=1.0,initial-scale=1.0,user-scalable=no"/>
<style>*{margin:0;padding:0}body,html{overflow:hidden}</style>
</head>
<body>
<div id="meet"></div>
<script src='https://meet.jit.si/external_api.js'></script>
<script>
var options = {
configOverwrite: {
disableDeepLinking: true,
enableWelcomePage: false,
hideLobbyButton: true,
prejoinPageEnabled: false,
startAudioOnly: true,
startSilent: false,
startWithAudioMuted: false,
startWithVideoMuted: true,
},
interfaceConfigOverwrite: {
DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
DISABLE_RINGING: true,
DISABLE_VIDEO_BACKGROUND: true,
DISPLAY_WELCOME_FOOTER: false,
HIDE_DEEP_LINKING_LOGO: true,
HIDE_INVITE_MORE_HEADER: true,
MOBILE_APP_PROMO: false,
RECENT_LIST_ENABLED: false,
SHOW_JITSI_WATERMARK: false,
},
roomName: JitsiWebInterface.roomName(),
jwt: JitsiWebInterface.jsonWebToken(),
};
var domain = JitsiWebInterface.domain();
var api = new JitsiMeetExternalAPI(domain, options);
function audioMuteStatusChangedListener(object) {
JitsiWebInterface.onAudioMuteStatusChanged(object.muted);
}
function videoConferenceJoinedListener(object) {
JitsiWebInterface.onVideoConferenceJoined();
}
function participantLeftListener(object) {
JitsiWebInterface.onParticipantLeft();
}
api.addEventListeners({
audioMuteStatusChanged: audioMuteStatusChangedListener,
videoConferenceJoined: videoConferenceJoinedListener,
participantLeft: participantLeftListener
});
</script>
</body>
Please read the Jitsi Handbook for a more detailed configuration.
Then you can create a JitsiWebInterface
:
object JitsiWebInterface {
internal const val url: String = "file:///android_asset/jitsi.html"
var roomName: String = ""
var domain: String = ""
var jsonWebToken: String = ""
var audioMuted: Boolean = false
private set
/**
* This function gets called from jitsi.html
*/
@JavascriptInterface
fun roomName(): String = roomName
/**
* This function gets called from jitsi.html
*/
@JavascriptInterface
fun domain(): String = domain
/**
* This function gets called from jitsi.html
*/
@JavascriptInterface
fun jsonWebToken(): String = jsonWebToken
/**
* This function gets called from jitsi.html
*
* Event notifications about audio mute status changes.
*/
@JavascriptInterface
fun onAudioMuteStatusChanged(value: Boolean) {
audioMuted = value
}
@JavascriptInterface
fun onVideoConferenceJoined() {
// you can implement this yourself
}
@JavascriptInterface
fun onParticipantLeft() {
// Hang up and show a message to the user that the call was terminated from the visitor.
// This will also be called when another mobile device is leaving the call, but you should react the same.
}
}
Now you can prepare the WebView, passing the Audio roomName
, domain
and jsonWebToken
to the JitsiWebInterface
and add the it to the WebView
:
webView.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
mediaPlaybackRequiresUserGesture = false
}
JitsiWebInterface.domain = audio.domain
JitsiWebInterface.roomName = audio.roomName
JitsiWebInterface.jsonWebToken = audio.jsonWebToken
webView.addJavascriptInterface(JitsiWebInterface, "JitsiWebInterface")
webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest) {
// We are granting all permissions requested by the WebChomeClient,
// because we already requesting for android.permission.RECORD_AUDIO permission
// without that permission recording audio would not work anyway
request.grant(request.resources)
}
}
webView.loadUrl(JitsiWebInterface.url)
When you accept an incoming call you must execute AnswerIncomingCallMutation
on the GraphQl interface and passing the iotDeviceId
you get from Call.Incoming:
graphQl.mutate(AnswerIncomingCallMutation(iotDeviceId))
Once the mutation is received on the Sensorberg backend, it will send a Stop Incoming Call
push notification to the other phones (connected to the same appartment).
To open a door you execute a GraphQl mutation passing the iotDeviceId
you get from Call.Incoming.
val stateInputList: List<IotDeviceStateInput> = listOf(
IotDeviceStateInput(action = "open", value = "true")
)
graphQl.mutate(SetIoTDeviceStateMutation(iotDeviceId, stateInputList))
When ending a call you must notify our backend GraphQl mutation passing the iotDeviceId
you get from Call.Incoming.
val stateInputList: List<IotDeviceStateInput> = listOf(
IotDeviceStateInput(action = "call", value = "false")
)
graphQl.mutate(SetIoTDeviceStateMutation(iotDeviceId, stateInputList)).mapToUnit()
This way of opening might change in future!