Intercom

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.

Overview

  • Requirements
  • Type of PushMessage(s)
  • Demo Implementation
    • Video
    • Audio (Jitsi)
      • Create static HTML
      • Configure Jitsi via JavaScript API
      • Open Jitsi in WebView
      • Accept Incoming Call
  • Accept Incoming Call
  • Open Door
  • End Call

Requirements

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.

Type of PushMessage(s)

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:

  • Incoming Call: Indicates an incoming call and contains all data that is required to establish that call.
  • Stop Incoming Call: This tells you to stop the ringing of your phone (because someone else has picked up the call).

Demo Implementation

The follwoing is just a suggestion of how to implement Intercom Audio & Video. We are not giving any support beyond this implementation.

Video

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)

Audio (Jitsi)

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.

Create static HTML

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.

Configure Jitsi via JavaScript API

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.
	}
}

Open Jitsi in WebView

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)

Accept Incoming Call

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).

Open Door

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))

End Call

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!