Integration in a native Android app

This guide describes how to setup an Android application that integrates with Seamly Web UI using a WebView and how to retain data across initialisations.

Important This guide assumes that there is a hosted app.html which uses the built-in appStorageProvider. Usually this will be provided in your implementation. For this demo we use https://developers.seamly.ai/clients/web-ui/demos/app/index.html.

A demo app for this example can be found at https://gitlab.com/seamly-app/examples/android-demo-app.

Components

Android's WebView offers everything we need to integrate to the client. We can load the prepared app page and use a JavascriptInterface to connect the client to our data storage.

Connecting the web page

To integrate Seamly Web UI we need to load the app page which hosts the Seamly Web UI into a WebView. This should be done in the onViewCreated (or similar) of the View that hosts the WebView. Note that when developing on your own machine, you can't use localhost and need to use your machine's IP. If you are testing on an emulator on the same machine, however, you can use the address 10.0.2.2:

String url = "https://developers.seamly.ai/clients/web-ui/demos/app/index.html";
WebView webView = (WebView) view.findViewById(R.id.webview);
webView.loadUrl(url);

As mentioned, we wil use a JavascriptInterface to connect to the storageProvider. To make this work, we need to explicitly enable JavaScript for the WebView. We should also register the JavascriptInterface. In this example we use the same object that hosts the WebView for the interface. If you want to use your own object substitute it for this in the code below.

WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "SeamlyBridge");

On the JavaScript side, the default appStorageProvider should be in place. At this point you can run the app and should be able to load a chat client, but it will not store anything yet. In the next two paragraphs we'll store and retrieve the data.

Storing data

In this example, we will be using Android's sharedPreferences to store data. The client will never store large amounts of data, so this might be fine for your needs too. If you have another data storage solution in place you can substitute this in the code below. Also verify if the solution matches the sensitivity of the data and your privacy settings.

On the app side, things are relatively simple. We have to declare a method that takes the data and stores it in our storage solution. Since we chose sharedPreferences and this only stores simple objects, we will pass our data in JSON format. This keeps our function simpler too, as it only needs a single argument:

@JavascriptInterface
public void setData(String data) {
    SharedPreferences sharedPreferences = this.getActivity().getPreferences(MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putString("seamly", data);
    editor.commit();
}

On the JavaScript side, the appStorageProvider will call this setData function.

You should be able to build and run the app again, but nothing will change. The data will be stored inside your app, but it isn't being retrieved yet. Let's tackle that next.

Retrieving data

We will need the stored data on initialisation. The appStorageProvider needs to retrieve the data directly when it is created. Due to the asynchronous way Android builds up its JavascriptInterface a custom function might not be in place at that time. But since we actually control the setting up and loading inside the WebView it is quite easy to pass our data on. All we need to do is go back to the onViewCreated where we initialized our bridge, check if we have some data and write it to the webpage:

String result = sharedPreferences.getString("seamly", "");
if(!result.isEmpty()) {
    webView.evaluateJavascript("window.seamlyBridgeData=" + result, null);
}

Note that we are creating a snippet of Javascript to directly execute on the client. This takes care of the JSON parsing, but it also means our result should be valid Javascript. Our simple isEmpty() check here also makes sure that we won't try to execute window.seamlyBridgeData=;.

The appStorageProvider uses the data left by our initialisation process.

Our app will now build and run and when you start a chat you can leave it and come back and the chat will continue where you left of. Our storage solution is working! But there is one small detail we overlooked...

Avoiding synchronisation issues

We only transmit the stored data on initialisation. This should be fine, since normally the storageProvider get function will only be called when the client starts. But if it ever gets called again, we would be providing it with older data as we never update the internal variable after initialisation. Fortunately this is an easy fix, we can simply return our data to the Javascript directly after receiving it. All we need to do is change our setData and add

if(!data.isEmpty()) {
  webView.evaluateJavascript("window.seamlyBridgeData=" + data, null);
}