Integrating unblu chat in your mobile app

1. Overview

1.1. General approach

Since the unblu technology is purely web-based, it can be integrated into native mobile apps (iOS and Android) using WebViews. To make this possible the following things must be done:

  • Setup app with WebView
  • Integrate unblu example HTML, JS and CSS files into the project
  • Add the initialisation of the WebView into the App
  • Add special configuration to the server

This documentation will go through these steps with an Android application as an example. Integrating the chat into an iOS app should work mostly the same with small adaptions.

This is to be regarded as an example. It can be used as base for a real project but must be adapted to fit the exact needs of the project and app.

1.2. What it looks like

Here are some screenshots of the Android example app.

  

2. Prerequisites of the native app

To start off an app is required that has an Activity or Fragment which holds a WebView. Basically the WebView can be placed anywhere however it is recommended to use the WebView as only child of the Activity or Fragment and give it all available screen space to avoid performance problems and guarantee that the content is large enough to be usable. The example below can be used as layout.xml for your Activity or Fragment.

layout
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/chat_webview"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>

The second step is to setup the WebView so that it will work with unblu.

WebView setup
@Override
public void onCreate(Bundle savedInstanceState) {
	...
	WebView chatWebView = (WebView) findViewById(R.id.chat_webview);
	// Without JavaScript nothing will work...
	chatWebView.getSettings().setJavaScriptEnabled(true);
	chatWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
	// Zooming should be avoided since it doesn't feel native and not necessary since all UI's are large enough to be usable
	chatWebView.getSettings().setDisplayZoomControls(false);
	chatWebView.getSettings().setSupportZoom(false);
	// unblu uses cookies to save the session states so they must be accepted.
	CookieManager.getInstance().setAcceptCookie(true);
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
		// Because the unblu server may be an other one than our base URL we must also allow 3rd party cookies.
	    CookieManager.getInstance().setAcceptThirdPartyCookies(chatWebView, true);
		// Mixed content mode is necessary since we will load resources that are integrated into the app as well as external ones from the unblu server.
	    chatWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
	}

	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
		// This will allow you to remotely debug the WebView with the Chrome browser if the app is configured to be debuggable.
 	   if (0 != (getActivity().getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE)) {
	        WebView.setWebContentsDebuggingEnabled(true);
	    }
		// Since we will load files stored in the app as well as external ones from the unblu server this must be enabled.
	    chatWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
	}
	...
}

This is all that is needed to setup the WebView to be able to run unblu. The next step is to integrate and load unblu in the WebView.

3. Integrating unblu into the app

3.1. Integration approach

There are two basic approaches of integrating unblu into the app. The first one is to simply load all of the content from the web and the second one is to integrate the base files needed directly into the app. Both approaches have pros and cons. In both cases the actual unblu application is loaded from the unblu server in the internet. The files discussed here are the base HTML in which the chat will run, CSS files to style the content as well as the javascript files that start and maintain the session using the unblu API.

3.1.1. Loading resources from a web server

Pros

  • Very flexible since changes to the scripts can be done server side and don't need an app update.
  • App size is not affected since no additional files are added.

Cons

  • Slower since the files first have to be retrieved from the internet
  • Slightly less secure since the files are loaded from the internet

3.1.2. Embedding resources in the app

Pros

  • Faster since some of the files are loaded directly from the device
  • Slightly more secure since the files loaded are directly located in the app

Cons

  • Less flexible: all changes to the resources on the device need an update of the app.
  • Size may become larger because the files are embedded.

 

Both solutions are a viable way to go. This example will show how to integrate the files in the app, serving them from an webserver is basically the same except that the main HTML file is directly loaded from the internet and the referenced javascript and css files don't use the "file://" protocol.

These are the files needed to integrate unblu into the app.

3.2. Main HTML file

The main html file is the file that is initially loaded in the WebView and will run, maintain and show the chat. Its most important job is to load the correct script files that start the session. Additionally it should display a loading screen until the chat session is ready.

main.html
<!doctype html>

<html lang="en">
<head>
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
    <title>Unblu Chat Demo</title>
    <!-- stylesheet for this html file -->
    <link rel="stylesheet" href="file:///android_asset/style/main.css">
    <!-- stylesheet that overrides styles of the unblu UI -->
    <link rel="stylesheet" href="file:///android_asset/style/unblu-override.css">

    <script>
         alert("Connect Remote Chrome Debugger now");
    </script>
    <!-- general util functions and poly-fills -->
    <script src="file:///android_asset/js/util.js"></script>
    <!-- wrapper of the unblu snippet that loads the unblu application -->
    <script src="file:///android_asset/js/unblu-snippet.js"></script>
    <!-- wrapper of the unblu api that allows direct starting of a chat session -->
    <script src="file:///android_asset/js/unblu-chat.js"></script>
</head>

<body>
    <!-- loader that is displayed until the chat session has loaded (see main.js) -->
    <div id="loader-container">
        <div>
            <div class="loader">
                <span></span>
            </div>
            <div id="loader-text">
                loading...
            </div>
        </div>
    </div>
    <!-- main script that contains callbacks to the native app and uses the other scripts -->
    <script src="file:///android_asset/js/main.js"></script>
</body>
</html>

 

3.3. Main JavaScript file

Corresponding to the main.html the main.js file does the actual initialization of the unblu application and provides functions that can be called from the mobile app as well as callbacks that should be implemented in the mobile app and provided to the WebView. This serves as an example and can be changed and extended to the needs of the actual mobile application.

3.3.1. Unblu initialisation

To allow the mobile application to control which unblu server and which unblu API token should be used, the main.js script expects its backing html file to be loaded with query parameters defining these.

unblu initialisation
mobileChatApi.initUnblu(window.getQueryParameterByName("apiKey"), window.getQueryParameterByName("server"));

So when loading the html file initially these query parameters must be added or the unblu starter script can't be loaded.

3.3.2. Function API

The following functions may be called by the mobile app by sending them to the WebView

 

window.mobileChatApi.initChat(username)

Initiates a chat session with the given username.
If a previous session was not terminated it will try to restore it. (Delete cookies to avoid this behaveiour)
Note: Must be called after the "document-ready" event.
@param username Username to be used for the chat session


window.mobileChatApi.terminateSession()
Terminates the current session.
If no session is active, nothing will be done.

3.3.3. Callbacks

The following functions should be implemented by the mobile application and made visible to the WebView under the following prefix: 

window.MobileAppApi.* 


window.MobileAppApi.onDocumentReady()

Called when the DOM document-ready event occurs.
Note: this is used instead of using the WebViewClient.onPageLoaded() callback 
since there are issues of it being called multiple times for pre Lollipop WebViews.

 

window.MobileAppApi.onSessionAlreadyActive()

Called when a session is being established and a previous session could be restored.

window.MobileAppApi.onNoAgentAvailable()

Called when no agent is available for chat.


window.MobileAppApi.onSessionStarted()

Called when a new session has successfully been started


window.MobileAppApi.onSessionEnded()

Called when the current session has ended. (Either through abort or terminate)

 

window.MobileAppApi.onError(String message)

Called for any error that occurred
 @param message The error message


window.MobileAppApi.getStringResource(String name)

Should retrieve and return the localised text for the given key.
This enables content of the WebView to display localised texted provided by the app. 

 @param name Key for the string resource to retrieve.
 @return Localised string representing the resource.

3.4. Additional files

The additional files will not be explained in detail here. Look into the source code to see what they do exactly. Here is a brief overview of what they are for:

  • unblu-snippet.js and unblu-chat.js utilise the public unblu api to provide the functions used in the main.js. They can also be modified to fit the needs of the specific project und unblu setup.
  • main.css contains styles for formatting and laying out the main.html page e.g. displaying and animating the loader. This also serves as an example that should be adapted.
  • unblu-overwidden.css hides and modifies some of the unblu user interface items which are not needed or not wished in the mobile chat scenario. This file should not be adapted.

3.5. Embedding the files into the app

To embed the discussed file into the app and make them available for the WebView, they must be copied into the projects assets folder. To keep the folder structure clean it is advised to use subfolders for html, js, img, style. etc. files.

How these files can be accessed will be discussed in the next chapter.

4. Binding unblu and the mobile app

Now that the WebView is setup and the resources needed are embedded in the app it is time to actually load the main.html and do the binding between the javascript running in the WebView and the native mobile application.

4.1. Providing callbacks to the WebView

The last step of initialising the WebView is to provide it with the documented callbacks that the JavaScript code can call. To do so simply a class with the respective methods and the @JavascriptInterface annotation must be programmed and set to the WebView. Each method may than react on the different events as wished by the app. Note that all of these callbacks are not called on the UI-Thread meaning that any UI action must be posted on it e.g. using getActivity().runOnUiThread(...);

The example application holds examples of what may be done in the callback methods. See: 

com.unblu.mobile.android.chatdemo.UnbluChatFragment.java
 
WebAppApi
private void bindWebAppApi(WebView chatWebView){
	chatWebView.addJavascriptInterface(new WebAppApi(), "MobileAppApi");
}
 
public class WebAppApi {
	/**
 	 * Called when the DOM document-ready event occurs.
	 * Note: this is used instead of using the WebViewClient.onPageLoaded() callback 
 	 * since there are issues of it being called multiple times for pre Lollipop WebViews
 	 */
	@JavascriptInterface
	public void onDocumentReady() {...}
 
    /**
     * Called when a session is being established and a previous session could be restored.
     */
    @JavascriptInterface
    public void onSessionAlreadyActive() {...}

    /**
     * Called when no agent is available for chat.
     */
    @JavascriptInterface
    public void onNoAgentAvailable() {...}

    /**
     * Called when a new session has successfully been started
     */
    @JavascriptInterface
    public void onSessionStarted() {...}

    /**
     * Called when the current session has ended. (Either through abort or terminate)
     */
    @JavascriptInterface
    public void onSessionEnded() {...}

    /**
     * Called for any error that occurred
     * @param message The error message
     */
    @JavascriptInterface
    public void onError(String message) {...}

    /**
     * Should retrieve and return the localized text for the given key.
     * @param name Key for the string resource to retrieve.
     * @return Localized string representing the resource.
     */
    @JavascriptInterface
    public String getStringResource(String name) {...}
}

4.2. Loading the main.html

Now the main.html file that we placed in the assets folder must be loaded into the WebView which will automatically initialise the unblu environment and then wait for an API-call to start the chat. Two things have to be taken into account when doing this.

1.) Query parameters

As described in the previous chapter, for this example, the main.html has to be loaded with query parameters for the unblu server and the unblu API key.
This may be done differently for real projects depending on how server and API key are retrieved. 

2.) Base URL

Since we are loading a local html file we do not have a valid origin if we just load it with a simple:

chatWebView.load("file://.../main.html");

Without a valid origin we will have problems with cross-document messaging used by unblu as well as with communication with the unblu server which requires a valid origin. 
To overcome these problems, the html file is loaded with a base URL which makes it act, like it was provided from the given URL and therefore provides a valid origin. This URL can be any existing or un-existing URL.

A second reason of why this base URL is important is the filtering of Chat requests in the unblu agent desk. The base URL provided here will be displayed in the Chat queue and allows filtering. This can be used as a powerful tool to filter only the chat requests from the mobile app.

Example Code:

loading main.html
private static final String LOAD_URL_FORMAT = "%1$s?apiKey=%2$s&server=%3$s";

@Override
public void onCreate(Bundle savedInstanceState) {
	...
	// Loads the main.html file from the assets and saves the content in a String
	String html = loadAssetContentIntoString("html/main.html");
	// Puts together the load url formed of the configured baseUrl, and the unblu API key and unblu server as query parameters.
	String loadUrl = String.format(LOAD_URL_FORMAT, baseUrl, apiKey, server);
	// Loads the html into the WebView.
	chatWebView.loadDataWithBaseURL(loadUrl, html, "text/html", "UTF-8", null);
	...
}

4.3. Starting a chat session

After the main.html is loaded the last step needed is to actually start a chat session which must be initiated from the mobile application.

start chat
private static final String JS_API_START_CHAT = "javascript:mobileChatApi.initChat(\"%1$s\");";
 
@Override
public void onCreate(Bundle savedInstanceState) {
	...
	// Loads the html into the WebView.
	chatWebView.loadDataWithBaseURL(loadUrl, html, "text/html", "UTF-8", null);
 
	// Sets a callback that starts the chat with the given username as soon as the document is ready.
	setOnDocumentReadyListener(new OnDocumentReadyListener() {
  	  @Override
  	  public void onDocumentReady() {
        //Init the Chat
        chatWebView.loadUrl(String.format(JS_API_START_CHAT, username));
  	  }
	});
	...
}
 
/**
 * Sets the given callback as document ready listener.
 * The listener will be called exactly once.
 * If the document is already loaded it will directly be called, 
 * otherwise it will be called as soon as the event occurs.
 * @param onDocumentReadyListener The listener to be called when the document is ready.
 */
public void setOnDocumentReadyListener(OnDocumentReadyListener onDocumentReadyListener) {
    if (documentReady.get()) {
        onDocumentReadyListener.onDocumentReady();
    }
    this.onDocumentReadyListener = onDocumentReadyListener;
}

interface OnDocumentReadyListener {
    void onDocumentReady();
}
 
public class WebAppApi {
    /**
     * Called when the DOM document-ready event occurs.
     * Note: this is used instead of using the WebViewClient.onPageLoaded() callback
     * since there are issues of it being called multiple times for pre Lollipop WebViews
     */
    @JavascriptInterface
    public void onDocumentReady() {
        Log.d(DEBUG_TAG, "onDocumentReady");
        // Use listener to guarantee that the method is only called once
        if (!documentReady.getAndSet(true) && onDocumentReadyListener != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    onDocumentReadyListener.onDocumentReady();
                }
            });
        }
    }
	...
}

This marks the last step needed on the client (app) side. Use the callbacks and API methods to observe and control the session flow.

5. Configuring the server

Unblu allows different configurations to be applied for different scopes. One of the scope options is the domain that the visitor is on. Since we are using a base URL for our mobile app, this allows us to do specific configurations for our mobile application using the domain scope.

The following specific configuration flags must be set for the chat to be displayed correctly in the mobile app. As of this documentation (unblu v4.0.0RC9) some of them are unofficial, not supported configuration properties that may change un-documented and should be used at own risk.

For more details on how the configuration can be integrated into the product please refer to the documentation of Advanced Unblu Server Configuration.

server configuration
<configuration>
	<global>
		<!-- your normal global product configurations-->
	</global>
	
	<!-- The base URL used to load the main.html allows us to specify a special configuration for the mobile app. -->
	<domain origin="http://mobile.chat.unblu.com">
		<!-- unofficial internal configuration used to display the chat in fullscreen mode on the visitor side -->
		<property key="com.unblu.cobrowsing.chatFullscreenEnabled">true</property>
		<!-- unofficial internal configuration used to prevent the unblu UI to adapt its size which is not wanted for this use case -->
		<property key="com.unblu.platform.client.uwt.VisualViewportRoot.forceScaleNone">true</property>		
		<!-- unofficial internal configuration used to prevent the unblu UI from hiding when the window is scrolled which is not wanted for this use case -->
        <property key="com.unblu.platform.client.uwt.VisualViewportRoot.forceNoHiding">true</property>
		<!-- unofficial internal configuration used to prevent the chat from animating its size wich is not wanted for mobile keyboard display and hide (as of 4.1.8) -->
        <property key="com.unblu.core.chat.animateChat">false</property>
		<!-- enables starting sessions with chat instead of co-browsing -->
		<property key="com.unblu.cobrowsing.startWithChatOptionEnabled">true</property>
		<!-- prevents the chat from directly starting with co-browsing -->
		<property key="com.unblu.cobrowsing.visualChatEnabled">false</property>
		<property key="com.unblu.cobrowsing.privateBrowsingEnabled">false</property>
		<property key="com.unblu.cobrowsing.cobrowsingButton">false</property>
		<property key="com.unblu.cobrowsing.filesEnabled">false</property>
		<!-- prevents the engagement tab / flap to be shown while loading the chat -->
		<property key="com.unblu.cobrowsing.hideOfflineFlap">true</property>
		<!-- allows the app to detect when the session is terminated and start a new one -->
		<property key="com.unblu.siteintegration.endUrl">RELOAD</property>
	</domain>
</configuration>

6. Example Application

A compilation of all described steps can be found in the attached Android Unblu Chat Demo app. If the server is setup as described above it can be used out of the box simply by adding the correct configuration in the

com.unblu.mobile.android.chatdemo.MainActivityFragment.java

class.

The app is based on the Android gradle build system and can be imported and used into Android Studio .

GitHub link: https://github.com/unblu/example-mobile-android-chat

 

How can we help?

Chat with us and we will take you through our site!