Main Technologies

As shown in our research, our main technologies are Vue.js (web app in JavaScript), Android Studio(Android App in Java), IBM Cloud DB2 (Cloud database), Java (for the backend and services API) and Google Firebase(media storage)

Vue.js
Android
IBM Cloud DB2
Java
Google Firebase

All Java components will be managed using the Maven build tool.

Maven

We will also be using the Java SpringBoot dependency which allows us to create simple RESTful APIs with little overhead code.

SpringBoot

Admin Web App

The companion web app to the IBM FISE Concierge allowing friends of (elderly) users to set up accounts and manage the details of all members in their circle. Admins have the ability to add and remove services from member accounts and view service usage history for each member.

Responsive Design

The web app is designed to be usable on any device with a browser (with JavaScript enabled) and as such required care into ensuring it worked on any (modern) browser on any screen size and orientation. To facilitate this, the mobile-first design pattern was implemented using CSS – the styles for each component (button, header, input etc.) are designed to be usable on mobile devices. Once the appearance was up to standard, extra cases were added (using CSS media queries) to make use of the space afforded by larger devices.

Click to enlarge An example of the CSS Media queries used An example of the CSS Media queries used

Several implementation iterations were carried out, testing the appearance on multiple browsers, screen sizes and orientations.

Accessibility

The web app must be user-friendly to all types of users, including those less able. To ensure this was true, standard HTML semantics were followed (e.g., using the <button> tag for buttons instead of div elements; splitting pages into section elements and ensuring headings followed the h1, h2, h3 etc. pattern in order).

This approach makes the website more screen reader friendly so more users can use the web app. Naturally, we are not experts on this, and our implementation is likely far from perfect, but accessibility was always kept in mind during the implementation phase.

Data Fetching

To communicate with the backend, the Axios Node module was used to make RESTful API calls to the backend; this was then wrapped in a set of methods to act as a further abstraction between the backend and the frontend. Axios is a popular and mature library but should we want to switch to another data-fetching library, only the content of the wrapper methods needs to be changed- the frontend can remain the same.

Given Vue’s reactive nature, the data provided on the website can be populated when it is ready (i.e., returned by the backend). To facilitate this, JavaScript’s Promise API was used with asynchronous data-fetching methods.

Naturally, API calls take some non-zero time to make and receive a response from – the website should not be frozen while waiting for this data otherwise it would completely hamper the user experience; this problem would worsen for users on slower internet connections.

In essence, when performing an API call with the axios.request() method, the object returned is a Promise. A Promise is an object representing the eventual completion or failure of an asynchronous operation (Promise - JavaScript | MDN, 2021). The backend response was wrapped in a Promise (by the axios library) which resolves into the JSON response when the backend responds; this happens automatically, handled by the Promise API.

As a result, we could make API calls and then set the required data when a response is received; once received, Vue’s reactive nature will then repaint the DOM to display this data.

Naturally, some methods require the data to be populated before they can run. To facilitate this, the await keyword can be used in asynchronous methods to pause execution in that method until the Promise resolves. However, it is important to note that this does not block the callee method as asynchronous methods return a Promise, so the callee is free to continue executing if needed.

In short, Vue’s reactive nature, coupled with JavaScript’s built-in asynchronous features, allows us to maximise the performance of the web app with little overhead code without idling, waiting for a backend response.

Click to enlarge Code snippet of the code used to communicate with the backend Code snippet of the code used to communicate with the backend

Vuex Store

Naturally, some data needs to persist between sessions. The Vue store (provided by the Vuex library) is a mechanism allowing for persistent data storage.

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralised store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion (What is Vuex? | Vuex, 2021).

In the web app, Vuex is used to store the username of the logged-in user. This prevents the user from having to login whenever they switch pages and also allows them to remain logged in even after exiting the web app. The username is cleared from the store when the user manually logs out.

Additionally, the store was used as an interface between the frontend components and the set of wrapper methods used to make axios API calls to the backend. Vuex is an extension of Vue.js, so the interaction between the two is natural and intuitive. Instead of the frontend directly making API calls, it interacts with the store; if the endpoints, or structure of messages changes, the frontend can remain the same – only the store needs to be modified.

To reduce bandwidth, images were also cached in the Vuex store meaning they do not need to be continually fetched from elsewhere whenever the admin visits the website. It is important to note that if more images were to be added, the Vuex store would need to be cleared (which can be done programmatically).

Click to enlarge Code snippet of the code used to interact with the store Code snippet of the code used to interact with the store

Media Storage

The web app uses profile pictures to differentiate between app users assigned to an admin (in case they have the same name). Icons for each service are used to make the user experience more aesthetically pleasing.

However, we decided we would not store these images in the cloud database as this would eat into our already limited IBM cloud storage quota (as we are on the free plan) which negatively affects the scalability of our solution – we would have to pay to increase our storage quota. Additionally, we would need to encode the images as binary text and then insert it; this would make database maintenance very difficult (e.g., we would not know if we’ve inserted the correct image as we cannot see it in the cloud database console).

Instead, images were stored in a Google Firebase bucket and accessed using Firebase’s JavaScript API. Accessing these images returns a download link which we can directly insert into an image tag in HTML. Images can then be easily modified as well as essentially providing us with more storage to work with for free.

In the cloud database, these images are referenced by their filename which the web app uses when searching the Firebase bucket.

Note that all images were converted into the .webp file format to reduce file sizes (and hence, bandwidth) with no loss in image quality. Images were also scaled down so their size was the maximum size the image would ever be when displayed on the website.

Click to enlarge Screenshot of Firebase bucket Screenshot of Firebase bucket
Click to enlarge Code snippet of the code used to access the Firebase bucket Code snippet of the code used to access the Firebase bucket

Marketplace

The marketplace allows users to search for services by either their name, or description.

Naturally, users may make mistakes with typing meaning a strict equality string comparison between the search term and each service name is inappropriate./p>

Instead, the similarity between the two was compared and returned a match if this similarity was above 75%.

To calculate the string similarity, an edit distance algorithm was used:

Click to enlarge Code snippet of similarity function Code snippet of similarity function
Click to enlarge Code snippet of the edit distance algorithm Code snippet of the edit distance algorithm

Android App

See implementation details for the app below.

Main Technologies

The app was programmed in Java using the Android Studio IDE and its built in features such as the Profiler.

Dependencies & Tools

We used several external libraries beyond the standard Java and Android libraries to help us develop and test our app. See the different sections below.

Android APIs

We made extensive use of the built in Android APIs and this was used for the main functionality of our VA, from making calls and providing notifications to opening URLs and providing speech synthesis.

This includes using the built-in Android speech recognition (which can be used offline for Android API Level 16 and above) and speech synthesis for our app. We used this as it was the simplest option given our app was native to Android. This provides the added benefit that as the Android speech recognition and speech synthesis improves, our app will automatically benefit, rather than having to maintatin and improve speech recognition and synthesis ourselves.

Generall speaking, using existing Android APIs saved us time from having to implement all the low level code ourselves. It also resulted in better code quality and ensured compatibility with different Android SDKs [see Compatibility Testing for more details]. The Android APIs are also well tested which is an important metric for quality assurance.

Built in SQLite 3 DB (For Alarms, Reminders and User Details)

We utilised the built in SQLite database that comes with Android for our Alarms and Reminders. We also used this to store a user's details after they connected to their admin.

We utilised the localised storage as it is fast, convenient, reliable and raises no privacy concerns. It can now also act as a framework for developers who may wish to utilise the localised storage for their own features, some of which may be privacy sensitive.

Built in widgets

We utilised several built in widgets such as DatePicker, NumberPicker and more.

These were used in some of our features. For example Alarms required widgets for choosing the date and time. and Timers required the NumberPicker widget. Reasons for using the built in widget are the same as for using Android APIs (see above).

However, we accept a possible disadvantage of doing this which is that customisation of built in widgets is limited. Though we acknowledge that our app may benefit from custom widgets design wise, we did not have enough time to code and test these (this may be an idea future teams can pick up on).

Java

Java 13

We used Java 13 to program our app as it was the most up to date Java version and provided many standard, well tested, well maintained libraries which we could use.

Data Structures

We used many data structures in our app. The most notable one was HashMaps.

This was used to store key value pairs after parsing the JSON response from Ask Bob which would then be used by the AskBobResponseController to decide which method in MainController to call.

We used HashMaps for their efficiency, as they provide O(1) time complexity for searching. This would ensure that we could respond to user requests more quickly. They also come with a variety of built in methods that were very useful, such as contains(), which was needed at several points to quickly check what sort of a request the user had made (Service API, Lookup etc). It also prevented the app from crashing due to NullPointerExceptions as we could check if the hashmap contained a particular key or not before asking for it through the get() method. Note that O(1) time complexity came into use here too.

Aside from this, we used other data structures such as ArrayLists and Strings to pass data around our app appropriately. These data structures, along with HashMaps, came with a nice interface and Stream API methods which allowed us to easily peform required operations on these data structures without having to implement them ourselves. They were also easier to work with than raw data structures such as arrays due to their interface.

We also used booleans, ints and other primitive data types. We used these when there was no need to use more complex data structures which made our app faster and less resource intensive.

Other Java Libraries

We used other standard Java libraries such as HttpUrlConnection (for connecting to the backend and Ask Bob servers) throughout the app. These standard libraries are also what allowed us to connect our app to the backend automatically.

Given that our backend server runs locally rather than on the cloud, the IP address would change for everyone who ran the server. This created an issue as to how we could program our app to connect to the backend server on any network for everyone as the IP would change every time. We came up with a solution where our backend server broadcasts its IP address and the app picks it up and stores this IP. This meant our app can dynamically connect to the local backend server on any network for everyone, without any input from the user.

Other Major Libraries

Espresso

This library was used to write Instrumentation tests. Please refer to the testing section for further details.

Robolectric

This library was used to write unit/integration tests. Please refer to the testing section for further details.

Monkey

This library was used for stress testing. Please refer to the testing section for further details.

AndroidJUnit4Runner

This library was used to run Instrumentation tests. Please refer to the testing section for further details.

org.json

This library was used to parse the JSON response from the Ask Bob Server.

Backend and Ask Bob Servers

The app makes use of the Ask Bob server to send user requests and receive a JSON response which the app can use to decide which action to take. We also make use of our backend server to connect users with their admin and provide them with the functionality this entails such as their history.

Minor Libraries/Technology Used

api.postcodes.io

This API is used to convert the user's latitude and longitude into a postcode for certain services such as Yell searches.

Play Services Location

This Google library is used as part of getting the user's latitude and longitude.

AskBob Integration

An integral part of our system is the Concierge Voice Assistant. This voice assistant is powered by the AskBob RESTful API which is deployed locally and will allow users to utilise the voice assistant within the app. This AskBob API receives the user's command from the Concierge app in plain text. It then employee’s natural language processing to decode the intent behind the command and then calls our Service API to retrieve the necessary information required to fulfil this request. This data is then returned to the app where the appropriate action can be triggered.

This AskBob API was developed using Team 39’s AskBob NLP framework. This framework is based upon the Python library Rasa which is designed for building chatbots. Originally, we had been utilising Spacy to perform our natural language processing. In this case, we designed and trained our own NLP Spacy model and were then running it as an API using the Python Library Flask. However, our clients requested that we integrate some of our services with the other 2 IBM groups (Team 39 and Team 38). As part of this integration, we were instructed to transition to the AskBob NLP framework for our voice assistant to ensure consistency across the FISE ecosystem. This involved transferring the services that we had already designed to the AskBob framework.

Structure

As mentioned above the AskBob API is intended to be deployed on a local network as specified in our MOSCOW requirements. The AskBob API is intended to receive a user’s command in plain text from the app which is acquired by having the app perform speech recognition on the user’s verbal request. This request is then processed, and the appropriate data required to fulfil the request is retrieved from our service API. For the AskBob API to perform this, it is reliant on also having the service API running locally.

Adding Plugins

Services are added to the AskBob API in the form of plugins. Plugins are collections of services and the general Concierge plugin consists of three main files: an __init__.py file, an actions.py file and a config.json file. Within these files, all the data required to implement a service is contained.

The __init__.py File

The purpose of the __init__.py file is to mark the plugin folder as a Python package.

The config.json File

The config file is used to define the skills within a given plugin. Skills refer to a specific service within the plugin. At its core, a skill is comprised of intent and action. The intent can be understood as what the user is attempting to achieve, and the action is the process that is triggered by specific intent. When defining intent multiple sample commands must be provided as this is what the NLP model will train from. Actions on the other hand will be defined within the actions.py file.

Additionally, certain skills may require data to be extracted from the user’s command. This data is referred to as entities and must also be defined in the config file. For more information on the syntax of a config file see Team 39's GitHub repository.

Below is a sample config file for the weather service:

Click to enlarge An example AskBob config.json file An example AskBob config.json file

The actions.py File

The actions.py file contains is where the actions for a given skill must be defined. Each action is defined as its own class. The code defined within this file will trigger when a given intent is recognised. For the majority of the Concierge an action will consist of extracting the necessary entities required by the service API and then placing a request to the specific service API endpoint to retrieve the necessary response. This response is then returned as a JSON data package with the following format:

Click to enlarge An example JSON response An example JSON response

Below is an action class for the weather service that is defined by the config class.

Click to enlarge An example AskBob actions.py file An example AskBob actions.py file

Services API

The API service interaction is designed to be highly extensible with two methods to adding new services.

JSON Schema

This Java package greatly simplifies the extension of Concierge's already rich API interaction ecosystem. It also allows for new services to be added in a language-independent manner, providing a simple and intuitive, yet powerful interface.

Adding a new service assumes, and requires, prior knowledge of the API, its endpoints, required URL parameters and response structure. Prior knowledge about HTTP status codes (and what they mean) used by the API are required for custom exception reporting.

For most services, the JSON schema should be sufficient to perform and parse API calls. However, there are some limitations with the schema; to circumvent these, the service can be implemented as a Java.

See the example schema below (the API key has been omitted):

Click to enlarge JSON Schema JSON Schema

The message field gives an indication as to what the natural language string returned will be.

More information on how this works and possible configurations can be found in our GitHub wiki.

Java Class

Naturally, there are some limitations with string parsing (detailed in the wiki).

To get around these limitations, a Java class can be used instead for more fine-grained control over the JSON response object parsing.

The new service will have to extend an abstract class, AbstractServiceRequest shown below with some omissions:

Click to enlarge Abstract Service Request Abstract Service Request

Note that this is to extend the Service API interaction, without any NLP or inclusion in the FISE Concierge ecosystem.

References

Developer.mozilla.org. 2021. Promise - JavaScript | MDN. [online] Available at: <https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise> [Accessed 26 March 2021].

Next.vuex.vuejs.org. 2021. What is Vuex? | Vuex. [online] Available at: <https: //next.vuex.vuejs.org> [Accessed 26 March 2021].

Robolectric.org. 2021. Robolectric. [online] Available at: <http://robolectric.org/> [Accessed 26 March 2021].

Espresso. 2021. Espresso. [online] Available at: <https://developer.android.com/training/testing/espresso/> [Accessed 26 March 2021].

Monkey. 2021. Monkey. [online] Available at: <https://developer.android.com/studio/test/monkey/> [Accessed 26 March 2021].

Postcodes.io. 2021. Postcodes.io. [online] Available at: <https://api.postcodes.io/> [Accessed 26 March 2021].

Play Services. 2021. Play Services. [online] Available at: <https://developers.google.com/android/guides/overview/> [Accessed 26 March 2021].

JSON.org 2021. JSON.org. [online] Available at: <https://github.com/stleary/JSON-java/> [Accessed 26 March 2021].

GitHub. 2021. UCL-COMP0016-2020-Team-39/AskBob. [online] Available at: [Accessed 29 March 2021].