`

Long Hoang Vu Android Multimodal Framework for Warehouse Management Application

Metropolia University of Applied Sciences Bachelor of Engineering

Information Technology Bachelor’s Thesis 27 October 2019

Abstract

Long Hoang Vu Author Android Multimodal Framework for Warehouse Management Title Application

Number of Pages 69 pages + 0 appendixes Date 27 October 2019

Degree Bachelor of Engineering

Degree Program Bachelor of Information Technology

Professional Major Mobile Solutions

Instructors Jarkko Vuori, Project Supervisor Tuomas Reunanen, Software Developer

The objective of this final year project was to create an Android framework that supports building mobile applications in warehouse environment which can be controlled through both tradition screen display and voice commands. From there, another goal was to create a proof a concept (POC) of a selected warehouse process to evaluate the quality of imple- mented framework. Finally, profound insights were collected from participants in order to decide whether the project was worth continuing.

To carry out the work, practical information was gathered from meetings which involved people from different departments in the company. The intention was to discover what the company anticipates in such framework and what criteria it should have to fulfill the require- ments. Then, theoretical knowledge to enforce the framework’s implementation was assem- bled from developers’ experience, programming community’s best practices, and books and blogs from well-known experts.

As a result of this study, the framework became one of the top priority projects in company’s milestones. It was expected to be an official development tool for developers to build robust and testable features with both screen and voice interaction. Consequently, the company would be able to distribute stable products in less time while earning more profit, and thus become more competitive in the market.

Keywords Android, voice, multimodal, framework, warehouse, logistics

Contents

1 Introduction 1

2 Current State Analysis 2

3 Material and Methods 3

3.1 Approach to Project 3 3.2 Multimodal Framework Criteria 4 3.3 Summary 5

4 Theoretical Background 5

4.1 Language and Architecture Criteria 5 4.2 Kotlin 6 4.2.1 What is Kotlin? 6 4.2.2 A Better Alternative to Java 7 4.2.3 Summary 10 4.3 Software Architecture 10 4.3.1 Clean Architecture 10 4.3.2 Model-View-Controller Architecture 13 4.3.3 Flux Architecture 17 4.3.4 Clean, MVC, and Flux Comparison 29 4.3.5 Summary 30 4.4 State Machine 31 4.4.1 State Interface 31 4.4.2 State Classes 32 4.4.3 Delegation to States 34 4.4.4 Summary 34 4.5 Domain Specific Language (DSL) 34 4.5.1 Definition 35 4.5.2 Why DSL? 36 4.5.3 DSL In Kotlin 41 4.5.4 Summary 48 4.6 Reactive Extensions (ReactiveX) 48 4.6.1 Observer Pattern and Reactive Behavior 48 4.6.2 and Composable Behavior 50

4.6.3 Asynchronousity and Threading in Android 51 4.6.4 Summary 58

5 Implementation 58

5.1 Overview 58 5.2 State Input 59 5.3 State Machine 60 5.4 States 61 5.5 Views and Voice 62 5.5.1 State Fragments 62 5.5.2 Voice Interfaces 63 5.6 Summary 64

6 Conclusion 65

References 66

List of Abbreviations

WMS Warehouse Management System. A software application that is designed to support and optimize warehouse functionality and distribution center management.

UI User Interface. The means by which the user and a device interact.

POC Proof of Concept. An evidence that shows a business proposal, design idea, etc. will work, usually based on an experiment or a pilot project.

OOP Oriented-object programming. A that bases on concept of objects encapsulating data, logic, functionalities within them- selves.

IDE Integrated Development Environment. A software suite that consolidates basic tools required to write and test software.

ORM Object-relational Mapping. A technique that converts data between object- oriented model and relational .

MVC Model-View-Controller. An architectural pattern that separates an applica- tion into three main logic components: model, view and controller.

MVP Model-View-Presenter. An architectural pattern that separates an applica- tion into three main logic components: model, view and presenter.

MVVM Model-View-View Model. An architectural pattern that separates an appli- cation into three main logic components: model, view and view model.

SQL Structured Query Language. A domain specific language that is used in programming for managing data held in a relational database management system.

XML Extensible Markup Language. A markup language that is used to share data across different system, such as Internet.

TLS Transport Layer Security. A cryptographic protocol that provides end-to- end security of data sent between applications over the Internet.

JSSE Java Secure Socket Extension. A set of Java packages that enable secure Internet communications.

IO Input / Output. A communication between an information processing sys- tem, such as computer, and the outside world, possibly a human or another information processing system.

FRP Functional Reactive Programming. A language paradigm that is used for describing dynamic information.

JVM Java Virtual Machine. A virtual machine that enables a computer to run Java as well as programs written in other languages that are also compiled to Java bytecode.

1

1 Introduction

Optiscan is a company who provides Warehouse Management System (WMS) based on Voice and barcode technologies to optimize logistics processes in warehouse, man- ufacturing and post operations. With over 30 years of experience in logistics business, the company has been succeeding in delivering high quality solutions that mainly focus on customer’s productivity. However, since the customer base has grown quite rapidly in recent years and customization requirements are different from one warehouse pro- cess to another, continuously providing qualified, maintainable and fully tested software has become a problem that needed to be addressed.

From a technology perspective, a solid and comprehensible architecture should be im- plemented so that developers could utilize to build robust and testable application. In order to achieve this goal, it was crucial for the architecture to contain a business-driven workflow separating visual part, or what user see and interact with, from business logic, or rules that constrains software functionalities. Also, from a business rationale, including a demand for increased profitability and flexibility in deliveries, a new technology was required to improve operators’ work in warehouse. This technology should consist of a possibility to use voice recognition as additional user interface (UI) in traditional handheld application, a possibility to use wearable technologies such as smart glasses or smart watch as augmented UI devices and a possibility to deploy business logic to new, evolv- ing technology innovations, as they come available in the market. Therefore, the objec- tive of this final year project was to create a proof of concept (POC) of a selected ware- house process to evaluate the quality of implemented framework.

As a result, a written report was conducted to analyze key design patterns of the archi- tecture followed by strengths and weaknesses that the architecture has offered in terms of business goals and development productivity. Finally, recommendations based a list of findings would be added for future development and enhancements.

2

2 Current State Analysis

As mentioned above, software quality is one of the essential factors that lead to the de- velopment of the project since there are quite a few problems regarding this area that needs to be addressed. It is well known that business logic is the backbone of an app that provides actual benefits to users and Android is somewhat a tool to deliver it. There- fore, it is vital for the company to ensure the logics behave correctly and hence needs several tests to cover as many use cases as possible. However, the very first problem preventing that from happening is a lack of proper architecture, which leads to a lack of testing. In fact, without a well-defined plan, every part of the code is highly tied together and cause writing test immensely complex. Consequently, instead of creating small and specific tests focusing on individual use cases, developers now must integrate unrelated Android platform codes involving external resources such as real devices and addition libraries into those tests. This not only introduces a large learning curve to new develop- ers but also makes even experienced developers, who have already been familiar to the project, either miss an edge case or break an existing feature when dealing with a mas- sive change. Besides, it is such an exhausted job to have someone perform manual testing every time completed feature or bug fix is completed and be sure all functionalities are still working as before.

Apart from technical details, competitive market is also the reason why the project is strongly pushed forward. That said, Global eCommerce business has put new requirements for logistics industry, including demand for increased profitability and flexibility in deliveries. For WMS, this means, among others, need to introduce new technologies to improve human operator work in warehouses, including smart glasses and wrist displays. However, it will be a challenge for the company if they want to bring some of the functionalities to such devices because the code base is only implemented for handheld applications and not customizable enough to move to another platform and for some solutions such as voice, the product often ends up including expensive external licensed software and exclusive hardware devices. Thus, with an early adoption of An- droid platform, the company has decided to use this advantage by starting an in-house Android multimodal project that will not only fit the demands nicely but also offer devel- opers a chance to have their hands on new and evolving technologies.

3

3 Material and Methods

In order to achieve the final objective, trial and error, one of the most common ways in software development to verify whether an idea is worth continuing, and SCRUM, an agile framework for addressing complex adaptive problems while productively and crea- tively delivering products of the highest possible value, were the methods utilized to form a research plan. Besides, due to the nature of software development, the material used were the company’s existing applications and invention from pioneer developers in the technology community. Accordingly, below are the steps in the plan used to commence the project.

3.1 Approach to Project

Firstly, each developer provided architecture suggestions and as written drafts. Because of limited resources and time, these drafts were exchanged, discussed and modified daily only within the project team and continued until all developers agreed on the final version, which was the initial specifications of the project. During this process, individual experience and expertise were used to gather information based on these important questions:

 What is wrong with current development approach?  What has been improved in recent projects that can be utilized?  What principles, architectures and best practices should be considered?  How long should it take for another developer to understand how the archi- tecture works?  How long should it take for another developer to implement a simple pro- cess base his understanding?  How easy or difficult to customize a feature with this new framework?

Secondly, based on the final draft, developers started to implement the first version of the framework while weekly inspection with CEO and team leader was conducted to keep track of the progress. Additionally, attempts on different architectures and principles along with bug fixes and small changes were done to increase the product’s usability

4 and stability. After accomplishing the prototype, a sample app was built to demonstrate unloading, one of the warehouse processes.

Thirdly, the framework concept as well as the sample app were introduced to the whole company to collect thoughts and comments. In this meeting, as the major goal is to eval- uate benefits of the product and see how it satisfy the market demand, technical infor- mation was restricted reasonably for targeting audiences consisted of sales team, mar- keting team and managers. Similarly, another meeting was organized to obtain all in- house developers’ opinion on the professional side.

Finally, by incorporating people’s remarks, the company saw that the outcome could bring many advantages, so they decided to bring the project into official development and to prioritize its work by creating an appropriate backlog with all items estimated. The author would take part in this process and then started the POC on smart glasses when- ever he sees fit.

3.2 Multimodal Framework Criteria

In order to choose the best fit architecture for the framework, there were a few criteria developers must pay attention to. First and foremost, the framework should provide the flexibility to use both voice and screen display towards one common business logic mod- ule. In other words, switching between multimodal modes should be only a matter of changing the UI and functionalities should always behave the same way. In fact, the only thing that could alter the logic is the features’ reformation. As a result, the trait would make the product more scalable in the future.

Next, simplicity was an important point to keep in mind so that learning curve is confined as much as possible. However, it did not mean that new technologies should be pre- vented from being a part of development. The implementation of modern libraries should be straightforward and include detail documentation on how they work. Therefore, other developers could understand the framework better and take less time to learn it.

Eventually, the components within the framework should be kept cleaner based on their responsibility. The idea was to avoid huge classes with mix-up logic and make the code

5 more independent of the specific tools. Thus, the software would become more stable due to high maintainable and testable capabilities.

3.3 Summary

During the process, trial and errors was a necessary method to try out different technical solutions to deal with current architectural problems and add voice functionality. However, the findings from the method would have not reached their full potential if the knowledge was only known to one person. Beside quality, velocity was also an vital criteria to evaluate the potential of the project. In order to speed up the pace, transparency among team members is a must. If everyone in the team is aware of what the others are doing, it is easier to review, discuss and track the progress. That was the main purpose of SCRUM when it was bring in the workflow. From time to time, increments came out as the result of small-scale and continous investigations that helped relevant parties to know whether the project was going to the correct direction. In conclusion, when starting a project, an agile methodology such as SCRUM or Kanban should be used as the base where other methods like trial an errors can rely on.

4 Theoretical Background

4.1 Language and Architecture Criteria

Since the project targeted Android platform, the selection was between two programming languages, Java and Kotlin. From experience, developers would want a language that could satisfy some elementary criteria, including concise and less verbose syntax and functional programming. In the past, the Android team had to use external plugins and written its own code to support such features. Fortunately, Kotlin as a new language has provided not only required but also other useful traits, while the Java version supported by Google still lacked expected features. Consequently, Kotlin was chosen as the main for the project.

6

Regrading to architecture, the most important criterion that the elected one should have was the testability. In fact, there has always been a problem in company’s products due to lack of testing. One of the reasons causing the issue was the heavy wired code in the applications that made it difficult to write efficient test. Imagining a method that have all network call, logic processing, and UI update performed in one place, it is a challenging task to write a proper test for that method. Hence, the architecture should have on sep- arate of concerns as its main focus principle. Furthermore, it was supposed to involve a minimum effort to build future applications with the framework in order to compete in the market.

4.2 Kotlin

4.2.1 What is Kotlin?

Kotlin is an open source, general purpose, statically typed programming language for the Java Virtual Machine (JVM), Android and the browser.1 The language object is to be concise, which drastically reduce the amount of boilerplate code; safe, which avoids en- tire classes of errors such as null pointer exception; interoperable, which leverages ex- isting library for the JVM; and tool-friendly, which can be built in any chosen Java IDE or from the command line.2

Kotlin’s first ever trait is the ability to operate on any platform or environment that Java runs since its main goal is to provide a better option to the popular ancestor in terms of conciseness, productivity and safety.3 Two of the most well-known places where Kotlin is used are server side code, or backend of web applications, and Android applications. Besides, with the support open source library like Multi-OS Engine4, the Java alternative can also run on iOS devices. Nevertheless, JetBrains’s ambition goes far beyond Java interoperability. As an example, it is possible to compile Kotlin into JavaScript, allowing the language to run on web browser.3 In brief, Kotlin focuses on an overall productivity enhancement during the development processes instead of a single domain problem.

The second trait Kotlin has embraced is statically typed behavior. Similar to Java, types of every expression in Kotlin are declared and validated at the compile time, contrasting to dynamically typed language where all types are resolved at run time. Although there

7 are pros and cons in both methods, static typing yields certain benefits that is worth considering:

 Performance: invoking methods is faster since there is no additional check is required to figure out the correct type at runtime.3  Reliability: crashes at runtime are reduced as the compiler has verified the correctness of the program beforehand.3  Maintainability: working with unfamiliar code is easier as one can know which object he is working with.3  Tool support: more useful IDE features are enabled such as refactoring, code completion, etc.3

The last trait offered by Kotlin is the support of functional programming alongside the object-oriented convention, whose key concepts are stated as follows:

 First-class functions: functions are treated like any other variable. For in- stance, a function can be passed as an argument to other functions, re- turned by another function and assigned as a value to a variable.3  Immutable objects: once an object is created, its state cannot be changed.3  No side effect: pure functions are exploited in order to produce the same results they run regardless of the outside world.3

As a result, functional programming grants developers a chance to write code that is more coherent, safer in multithreading and easier for testing.

4.2.2 A Better Alternative to Java

This section focuses on Android development where the support for Java version is lim- ited to 7, which does not contain latter improvement and new feature for the language. When choosing the language to conduct the project, two main factors were considered: how much effort it takes to shift to Kotlin and what problems in Java that Kotlin has ad- dressed.

Related to language paradigm, both languages back object-oriented programming (OOP) and statically typed style. Therefore, a Java programmer can still follow the Java coding style and practices if one would like to without immense learning curve. It is nor- mal to write Java in Kotlin for a person who is trying Kotlin for the first time. The team

8 believed that after a time of usage, their competence would increase and thus be able to embrace more advanced Kotlin’s features. Moreover, Kotlin has a great interoperability with Java as one can call Kotlin codes in Java and vice versa. However, it does not mean that the languages are completely compatible. There are a few features in Kotlin that needs some workaround to be able to use in Java. Besides, most of the Integrated De- velopment Environments (IDE), like Android Studio or IntelliJ, provide tool to convert Java to Kotlin automatically, which is very convenience.

One of the benefits the Java alternative has given is the ability to write more coherent code. For example, in order to avoid Null Pointer Exception, which occurs when a method or property of an empty object is accessed, a common approach is probably looked like the example below: if (a != null) { if (a.getB() != null) { if (a.getB().getC() != null) { // Do something } } }

// Or if (a != null && a.getB() != null && a.getB().getC() != null) { // Do something }

Listing 1. Null-check procedure in Java

In Kotlin, the syntax is much shorter: a?.b?.c?.apply { // Do something }

Listing 2. Null-safe operation in Kotlin

The question mark between properties indicates a, b, c as a nullable type, and the whole statement will be executed the whole statement if all of them exists. Additionally, there is no more get words when accessing the values because Kotlin has simplified them. Therefore, if an object has a method called getData, it can be invoked from code as

9 object.data. Another example which is worth looking into are default arguments. Partic- ularly, function parameters in Kotlin can have default values, which are used when a corresponding argument is discarded: fun foo(bar: Int = 0) foo() // Called foo function. Default value 0 is used. foo(1) // 1 is used instead of default value 0.

Listing 3. Default value for parameter in Kotlin

On the other hand, to have a parameter’s default value in Java, one will have to declare two functions instead of one. This is often known as method overloading: public void foo(int bar) public void foo() { foo(0); // Called prior method with default value 0. }

Listing 4. Overloading methods in Java

Overloading methods allow two functions, without return type in consideration, with the same name but different parameters to co-exist. The above listing may be trivial with one parameter. However, if there are multiple parameters, the code is immediately cluttered with methods whose sole purpose is to provide default values.

Finally, there are quite an amount of issues in Java addressed in Kotlin, including:

 Null references that are controlled by the type system.2  Exclusion of raw types2  Arrays that are invariant2  Function types that opposes Java’ SAM-conversions2  Use-site variance without wildcard2  Exclusion of checked exceptions2

Explaining these functionalities in detail is out of the scope of this thesis, except for those used in the project, which will be clarified further in other section. However, the main idea

10 is to demonstrate how carefully Kotlin’s inventors has planned when developing the lan- guage. Obviously, they want to remove the impediments that developers have Java when creating applications.

4.2.3 Summary

It is not guaranteed whether Kotlin is the replacement for Java. In fact, many developers still feel comfortable with Java and find Kotlin features not that necessary. In spite of its rising popularity, Kotlin remains another option beside Java to grant developers oppor- tunities to try out different solutions. Nevertheless, in the project context as well as Optiscan, Kotlin has delivered perceptible results in regular product as well as the multi- modal framework.

4.3 Software Architecture

Software architecture represents fundamental structure of a software, which often con- sists of components, relations among them, and properties of both components and re- lations. To create such structure, the architecture lays out a set of principles to make all crucial design decisions that the system should follow to be organized and successful. The design decisions provide a conceptual basis for system development, support and maintenance. Although there are various architectures in developers’ community, the thesis only concerns well-known ones which focus on .

4.3.1 Clean Architecture

Clean architecture, defined by Robert C. Martin, is an architecture that focused on the separation of concerns by dividing the software into layers, which is:

 Independent of frameworks. The architecture does not count on any exter- nal libraries or frameworks. This allows the architecture to treat such details as tools rather than put itself to their limited constraints.5  Independent of UI. UI can be changed independently from business logic and vice versa. For example, a web UI can be replaced with a mobile UI without touching the business rules.5

11

 Independent of database. Database can be swapped between relation or non-relational database without affecting other part of the architecture. For instance, instead of using SQLite, one can use MongoDB or CouchDB.5  Independent of external agencies. The business rules is not aware of any- thing about the outside world other than the specification applied on it.5  Testable. The business logic can be tested without any UI elements, data- base or server.5

The entire idea can be summarized as the figure below:

Figure 1. Clean architecture and it’s layers

The architecture is broken down into four main layers, consisting of enterprise business rules, application business rules, interface adapters and frameworks and drivers. The layers work to together in harmony by following one dependency rule: source code de- pendencies can only point inwards.5 In other words, nothing in an inner circle can know about anything about an outer circle. The main target is to protect the business rules from outside changes as much as possible.

12

4.3.1.1 Entities

Entities are the enterprise-wide business rules, which are usually seen in form of busi- ness objects in an application.5 They encapsulate the most general and high-level rules that are least likely to change when other circles change.5 For example, one would not expect the entities to be altered when the app’s navigation or security is modified.

4.3.1.2 Use Cases

As the name inferred, this layer implements all the use cases of the system, which is also known as application specific rules.5 For instance, one of the use cases can be adding an item to the to-do list or reserve a ticket for a movie. The use cases are respon- sible for coordinating the data flow in and out of the entities and instructing those entities to use their business rules to achieve the goals of the use case.5 Same as entities’ layer, this layer is not affected by UI or database changes. However, although changes to ap- plication’s operation will not concern the entities, they will definitely affect code in the use cases.

4.3.1.3 Interface Adapters

The third layer contains a set of adapters that convert data from the business layers into the format that is most convenient for external agencies such as Web and Android or persistence frameworks like the database.5 Similarly, if there is a desire to use other external services, the adapters are also subjected to transform those data into internal form. Therefore, data in this layer is usually displayed to user or transferred across inter- net. Additionally, developers often take advantage of other architecture like Model-View- Controller (MVC), Model-View-Presenter (MVP) or Model-View-View Model (MVVM) to handle the conversion.

4.3.1.4 Frameworks and Drivers

The outermost circle is composed of tools and frameworks that have been mentioned in previous sections. They are the database, the web framework, the mobile platform, etc.

13

These are the details the one will want to keep separate place where they can do little harm.5

4.3.1.5 Data Flow

To communicate between layers, each of them is obliged to have an input and an output port so an outer circle can send and receive data from an inner circle’s ports. As shown in Figure 1, the flow of control goes inward from a controller towards an use case and then outward from the use case towards a presenter by using the use case’s input and output port. Furthermore, during the flow, the interior layers should not reference to the exterior ones directly since it will break the dependency rule. In order to achieve this goal, the ports can be built from some patterns such events or interfaces. For instance, in the circumstance when the use case needs to call the presenter, the use case calls an interface in its’ layer and have the presenter implement it.5

According to Robert5, data that crosses the boundaries should be simple data structures, like a data transfer object or arguments in a function call. Particularly, only isolated and uncomplicated data structures are permitted to pass the borderline. The reason for this is, for instance, when a database framework returns a special data structure, which is RowStructure in this case, it should not be passed to inner layer because some proper- ties of object is not relevant to the layer at all.

4.3.2 Model-View-Controller Architecture

MVC was originally invented by Trygve Reenskaug while he was a visiting scientist at Xerox Palo Alto Research Center in 1970s.6 The archiecture became so popular that it has evolved into various forms in the later years. Indeed, the programming community has either modified or enhanced the concept in accordance with their needs. One of the example is the replacement of controller with other components such as presenter an view model. Therefore, a generic term called Model-View-* (MV*) is used for representing types of architecture that includes model, view and a special component to connect the those two. The intention behind these architectures is to organize the code

14 by its purposes, which often includes code that holds the business rules, code that visu- alizes the application and code that controls the functionality.

4.3.2.1 Parts of MVC

Although the definition of MVC has grown a lot since its’ first introduction, let’s look at the initial explanation. MVC is an architecture that contains three major components, which are:

 A model that represents the knowledge. The model can be a simple object, or it can be some structure of objects. Especially, there should be a one- to-one correspondence between the model on the one hand, and the rep- resented world as perceived by the user on the other hand.7  A view that visualizes the model. The view highlights certain attributes of the model and thus acts as a presentation filter. Moreover, the view is at- tached to its model which it can get necessary information from and send appropriate message to.7  A controller that links the user and the system. The controller provides the user with input by arranging relevant view on the screen while translates user output into convenient messages and passes these messages to one or more views.7

From a personal perspective, the communication between the components is rather mul- tidirectional and complex. To demonstrate the point, here goes a figure illustrating the thesis author’s perception:

15

Figure 2. Communication route inferred from Trygve definition

As disclosed before, the architecture has done an exceptional work in segregating code by its functionalities. However, the connection within design is still somehow confusing to readers like the figure has shown. Because there is no uniform direction between components, data can be manipulated anywhere in the chain and it is not considered a good practice. Therefore, the next section will be spent to cover this matter.

4.3.2.2 Data Entry Points

As demonstrated in prior section, data flow in original arrangement does not have any restriction as the flow comes from anywhere. There is nothing wrong if the architecture has a few components in the application, yet it is hard to maintain when there are dozens of them since the routes are tangled together. Stephen6, however, depicted the first MVC as below:

16

Figure 3. Stephen6’s interpretation of MVC

In the figure, the interaction between controller, model, view is much better as there is a single flow throughout the whole architecture: user interacts with the controller; the con- troller modified the model; and since view is observing the model, the view gets updated. If this is the true meaning behind the Tregve’s MVC, it is a decent design to follow. Ac- cording to Martin8, the primary benefits of the original MVC is a separated presentation, which he stated like this:

Ensure that any code that manipulates presentation only manipulates presentation, pushing all domain and data source logic into clearly separated areas of the program

In recent years, technology like JavaServer Pages or Android has again modified the workflow of MVC:

17

Figure 4. Bidirectional data flow in Android MVC6

In this new version of MVC, there is no longer relationship between view and controller. Instead, they communicate through controller as a mediator. All the code to link the view and model is removed and moved to the controller. It is a legit argument to not allow the view to be aware of the model so that it can focus on more on how the visualization looks like instead of which data should be shown on the screen. Nonetheless, in Android, for instance, where Activity plays the role of controller, become a monstrous class because now it has to take care of binding logics.

In conclusion, every solution has its pros and cons. It is the developers’ job to keep the code base as sane as possible. For example, to resolve the monstrosity Acitivity in Android, one can create another class to take over the code that binds the view and the model.

4.3.3 Flux Architecture

Flux is a programming architecture proposed by Facebook8 that leverages unidirectional data flow to develop client-side web application. Additionally, it is not a complete software package or framework, but instead a set of patterns aiming to cope with application’s scalability when sophisticated needs arise.9 In fact, Adam9 stated that Flux and its imple- mentations only serve as a canonical foundation for people to perceive how the mechan-

18 ics are supposed to work and adopt it the way they see fit. By combining different pat- terns efficiently, Flux has established better solutions in data entry points and state man- agement that other architectures could not provide.9

Since Flux is one of the keystones in the project, the main focus of this topic is to get familiar with core ideas in the architecture, without diving into any implementation detail. First, one will go through definitions of those basic components that make up the infra- structure and the relationship between them. Next, principles which Flux employs are introduced meticulously in order to see how they can solve issues that typically emerge when developing an application. Finally, a brief summary on Flux will follow up to con- clude the matter.

4.3.3.1 Flux Components

Flux possesses three fundamental components representing each layer respectively, in- cluding the dispatcher, the stores and the views:

Figure 5. Important components in Flux8

Above figure describes the primary mental model for Flux programmer where actions are data container and the others are self-reliant nodes with distinct inputs and outputs.8 Actions are usually triggered by user interactions and then dispatched to a center dis- patching hub called dispatcher. Thereupon, they continue to be forwarded to callbacks that stores have registered with the dispatcher. Within those callbacks, stores react to actions which are pertinent to the state they are responsible for by employing suitable

19 logic. After the states are updated, an event is sent to notify views about changes in the data layer and the view hierarchy should re-render itself.

4.3.3.1.1 Actions

Action is an object that embodies a single functionality in an application and can be de- scribed in a natural language statement9, such as:

 Fetch user’s posts  Comment on a post  Create a post  Filter posts related to dogs

Typically, an action is comprised of a name or type and a payload. To clarify, type helps to tell the purpose of the action so that the stores can identify when it comes; while pay- load holds pieces of data the stores may need to handle the action. In other words, one can think of action as a mail parcel, whose internal content does not have much meaning until it reaches the destination.9 Besides, actions are the vital part of the system since they are the only reasons that changes can happen. Above all, the design imposes on actions as the only way to bring data into the system without any exception.

Given Figure 5, it can be concluded that not only are actions be prompted from user interactions, but they also are provoked by external elements. For example, server can fire an action when it has some updates for the application or a request to it has failed because of network connection. Actions, in turn, are often generated by semantic helper utilities called action creators, which after creation will send resultant entity to the dis- patcher through the method that it exposes.9

4.3.3.1.2 Dispatcher

Dispatcher is the central location where every action must arrive before they are con- sumed via the callbacks each store has registered in that dispatcher. Nonetheless, it has no intelligence of its own but distributing actions to store components.8 At an architecture

20 level, the dispatcher is not acknowledged as a separate but instead a pseudo layer be- cause knowing an action will make its way to all the stores is more imperative than how it is dispatched in detail.9 Here is a figure depicting the so-called “Single Dispatcher” in Flux architecture:

Figure 6. Dispatcher receives actions and dispatches them to stores9

Having said that, it still plays a key role as the application grows. One evident use case is when one store depends on data of another store; the dispatcher then invokes regis- tered callbacks in a specific order so that those stores can update themselves properly.8 According to Adam9, one golden rule is worth noted: the paramount arbiter of data de- pendencies is the dispatcher.

4.3.3.1.3 Stores

Stores in Flux is the application state and logic master, meaning they are exclusively in charge of transforming data and holding the states. In brief, no other external components or logic can carry or make decision on the states except the stores.9 The whole idea of stores is to encapsulate and modify data within one particular place so that information can be easier to track down when there is a need. Similar to dispatcher toward action, stores are the hubs of the entire architecture’s knowledge. Nevertheless, stores do not express a single record of data like object-relation mapping (ORM) models but instead manage state for a particular domain of the system. One clear example is a Facebook’s Lookback Video Editor that has a TimeStore to keep track of the playback

21 time position and play back state and a ImageStore to keep track of a collection of images.

Figure 7. A demonstration of an action followed by a store changing state9

Figure 7 reveals a workflow showing new data comes to stores via actions. As a result, stores cascade received input to suitable internal methods, update themselves and fi- nally broadcast an event for views to know that states have been adjusted. To summa- rize, states inhabit stores and only there are they altered, forging another golden rule to remember.9

4.3.3.1.4 Views

While views are merely an external piece outside of Flux, their role in the application is prominent. Beside the main responsibility to interact with users such as displaying infor- mation, receiving clicks, etc., they are the last stop of the data flow. In contrast to their counterpart in typical architectures, views in Flux respond to events by putting the de- rived data into actions that will be sent to stores by the dispatcher instead of allowing them to reach other event handlers without any constraint. MVC, for example, has its view invoke controller, model or another view in response to a user click while Flux view only create an action out of that event.9

React introduces a special view type close to the top of the view hierarchy called con- troller-view, whose primary job is to glue data with descendant views by listening to events emitted by the stores.8 In fact, this kind of view aims to retain control-like behavior on top of the hierarchy, which allows child views to be as pure as possible without any logic handling except displaying information. However, it does not commonly bring great benefits if many controller-views are added deeper to the hierarchy because odd effects

22 may occur due to complexity of multiple data updates along the way and conflict with single entry point. Regardless of potential and issues mentioned, ones should remember dispatching an action is the only way data flows out of the views.9

4.3.3.2 Flux Principles

4.3.3.2.1 Unidirectional Data

One of the matters in present applications that the architecture has been able to address is data flow direction in a system because though it may be straightforward to recognize the entry point and termination point of the flow, it is challenging to reach one point from another.9

Figure 8. A simplified version of a data flow9

As Figure 8 illustrates, the middle is a variable that can end up in diverse forms and hence is required to be designed properly. Typically, the clearer that part is, the more control of data an application gains. An example design is shown below:

23

Figure 9. An example of how the middle flow is designed9

In preceding figure, the intermediate components can exchange data to and from each other in a multidirectional way. Consequently, there is no guarantee that the latter data is processed in a same way as the previous one and thus results might not always be produced as one would expect. Despite the convenience this kind of arrangement gives in term of passing data around, the components have a high chance of falling out of sync from one another9. Generally, it means such design can generate inconsistent data be- cause of possible ordering bugs and cause atrocious experience for user. On the con- trary, Flux concentrates on an idea of enforcing data flow on a single direction, which manages to prevent components from updating data in a tangled order that breaks the system:9

24

Figure 10. Unidirectional data flow architecture in Flux9

The flow above brings out many crucial benefits that developers can take advantage of. For instance, having information run in a sole direction will save a great amount of time not only when tracing root cause of a bug but also when adding a new feature since the movement is predictable. As a result, programmers are more capable of sensing and anticipating issues as well as design impediments before they even confront them. For this reason, companies can secure more resources when delivering functionalities to users.9

To simplify the concept, a unidirectional data is a data flow that has both starting and finish points clearly defined and there should be no circular flows between these two points. When data completes a trip from the beginning to the end, it is called an update round. These update rounds are accountable for updating the entire application’s state and will not be interrupted by other actions being dispatched.9 However, the update round will expand as the application grows. What this means in Flux is that the number of stores may increase in a large scale, leading to inevitable dependencies between stores. Flux complements the unidirectional data idea in this case by having the stores’ der registered in the dispatcher so that after the first store is updated, the next store can

25 use previous store data to finish modernizing itself. As a result, all of the updates is able to happen in the same update round.

4.3.3.2.2 Explicit Over Implicit

One practice often used when programming in common architecture patterns is hiding details behind abstraction. Based on different scenarios, this approach can bring certain advantages, including reusable pieces of code, allowing parts of logic to be executed automatically, separation of concerns, etc. On the other hand, as the system grows significantly, so does the hidden complexity. After a while, it becomes arduous for developers, who are maintaining the software, to become fully aware of the overall operation. In order to solve this scalability issue, Flux has proposed a few solutions with ”explicit over implicit” as a standard rule.

Firstly, Flux pays attention to how components communicate with each other. In web and mobile applications, interactions with users are eventually interpreted as events and whichever element is interested in such events should subscribe to appropriate ones. In spite of different variants in programming, Observer/Observable is the common mechanism to handle such connection, especially in front-end development where UI elements are event sources that is observable and can be observed by controllers to execute a business logic. For instance, after pressing a login button, a notification is conveyed to a controller that is actively waiting for events and from there an authentication request is made and sent to server. Nevertheless, if there is no restriction on how freely segments can talk to each other, it induces a few drawbacks as the system grows because the data flow becomes harder to maintain, conflicting with pre-existing components when adding a new one and high chance of missing events. Instead, Flux has overcome the problem by sustaining a static messaging infrastructure that all notifications are distributed by an event dispatcher to every component and it is components’ responsibility to decide those messages that are relevant.9

26

Figure 11. Visualization of how Flux dispatches events to components9

As shown in Figure 11, components are treated equally by the dispatcher so that devel- opers do not have to worry about which event to subscribe to. Furthermore, it is possible to define dependencies among them to allow the ordering of messages without touching sending and receiving foundation.

Secondly, actions are only one mean to trigger any state change. In other words, if some- thing happens, it was the result of an action being dispatched. When an user interaction is supposed to alter the underlying data, it is converted into an action before any logic is executed. In fact, actions are the lightweight objects describing every functionality that the application offers. By skimming through existing actions, newcomers are able to un- derstand current features in the system, which make maintenance work easier.

Finally, all state changes occur in one place: the stores. This is intentional by Flux to keep related logic near each other because close proximity prevents jumping back and forth between places when figuring out where the transformation takes place. On the contrary, the implementation introduces a trade-off regarding the separation of concern principle. Eventually, the stores grow into monolithic controller where logic and data re- side together. However, Adam9 felt that there is nothing to gain by scattering them in separated component but instead one can utilize numerous of existing techniques to solve the problem elegantly.9

27

4.3.3.2.3 Layers Over Hierarchies

A given application can be divided into several features and each of them can form its own hierachy:

Figure 12. Features with their own hierachy of controllers, models and views9

The problem with this setup comes up when a feature begins to depend on another feature as states between two hierarchies needs to be in synchronization. A typical practice is to have a component in one hierarchy declare an arbitrary dependency to another component in another hierarchy and keep doing that as far as a new inter- hierarchy dependency is required.9 However, like other problems mentioned before, this can bring out some side effects in managing the workflow.

Another matter concerning hierarchy in architectural design is the depth. Not only does depth relate to the hidden complexity issue mentioned in previous section but also the scalable capability in any direction. That is, within a level in the architecture tree, it is possible to add other components:

28

Figure 13. Depth of a hierarchy expands in two dimension9

Figure 13 demonstrates the whole system which will grow both vertically and horizontally when new requirement is ordered. This, in turn, may authorize inputs to enter anywhere at any level if the data flow is not configured properly.

Flux tackled above problems by dividing components into layers. As a consequence, it aids a number of benefits to ease the development process, such as clear boundaries around views, communication routes and domain logic in an application. Likewise, Adam9 believed that applying rules on layers is much simpler than on each component as it requires less effort for the former than the latter.

Figure 14. Three main layers and their communication direction in Flux9

29

The three blocks in prior figure represent the main layers in Flux architecture and they will not change as the number of components increases. How data is transferred and transformed remains the same across newly added component. In addition, views are independent of data processing and they only have to care about which information is valuable and how it is displayed to users.

4.3.4 Clean, MVC, and Flux Comparison

Let’s table a quick look at the differences among three mentioned architectures:

Clean Architecture MVC Flux

Architecture type Layer (4…n layers) Hierarchy Layer (3 layers)

Data flow Multidirectional Multidirectional Unidirectional

Separation of Con- Yes Yes Yes cerns

Complexity (scale 3 1 2 of 3)

Table 1. Comparison among Clean Architecture, MVC, and Flux based on their common char- acteristics

Clean architecture conforms to a set of rules that concentrate on separation of concerns. By separating the code base into layers and following the dependency rule, the applica- tion is intrinsically testable and thus is more stable. Moreover, when a framework be-

30 comes outdated, it can be replaced with minimum effort since the layers are highly de- coupled from each other. However, it requires quite an endeavor to set up the architec- ture at the beginning to set up the layers and connect them together. Moreover, if it is squeezed into a single mobile application, developers may have to jump back and forth between code files to add a new feature. In general, the architecture is best fit for more complex systems, where, for instance, a common logic module is used to build desktop, mobile, and web application.

Same as Clean architecture, MVC also direct its intention towards concern segregation, but in a smaller scale. Due to its simplicity, the invention has been doing a remarkable job in not only web but also mobile application. Besides, numerous variations of the ar- chitecture were devised so that the architecture, now with proper communicating chan- nels, is as clean as the prior solution. Nevertheless, as previously mentioned, it is better to have MVC as a part of the big picture to coop with data presentation and let another layer handle business logic.

Flux is another choice that should be considered when starting a new software develop- ment process. The architecture itself is not an ideal implementation because it does not mean to be so. Identical to other architectures when they were invented, Flux was born to resolve contemporary architectural problems and enhance productivity and quality with many excellent practices and patterns. Developers can embrace Flux based on their needs, whether it is following the patterns, selecting an already-made Flux library or im- plement their own solution.

4.3.5 Summary

There is no such concept as perfect architecture. Each architecture has its own pros and cons. Generally, if an architecture is meant for optimizing an aspect of programming, it must sacrifice other values. One example is the Clean architecture, which was used in one of the company’s project. Despite the clear separation of concerns and testable ca- pabilities, some developers found it far more complicated for an ordinary application. Therefore, Flux satisfied certain criteria in order to become the most applicable choice, which are:

31

 All components are encapsulated to three main layers, where each of them only has a single responsibility  Code base is predictable thanks to one direction data flow  Application’s overall functionalities can be interpreted using simple actions  Business logic and UI are separated

As a result, the project’s participants agreed Flux is a great fit for development and hence have chosen it as a skeleton for the whole framework.

4.4 State Machine

Regardless of differences among mobile applications’ usability, users need to qualify for a certain of requirements in order to perform an action. For example, to buy a movie ticket, one must go through a handful of stages including choosing a movie, booking a time, reserving a seat, etc., and they are related in a way that the later stage expected the former stage to be carried out before it can continue. The same applies to warehouse processes where each process contains several steps to complete a specific task and each step demands conditions that worker can enter or exit. Thus, the entire concept can be comprehended as a finite state machine:

 There is a fixed set of states the machine can be in.10  The machine is supposed to be in one state at a time.10  A sequence of inputs or events is sent to the machine and then delegated to corresponding state.10  Each state handles specified inputs and results in appropriate states.10  Changing between states can be completed by using transitions10, which contain the target state and all information or data that state may use.

4.4.1 State Interface

Although each state is separate, they should conform a common interface containing methods that state machine can forward inputs and request resultant states while logic is encapsulated into their own implementation detail.

32

interface State { var id: String fun onEnter(Object object) fun handleInput(Object object, input Input): State? fun onExit(Object object) }

Listing 5. A Kotlin interface represents methods a state can have

From the code snippet above, onEnter and onExit methods will be called respectively when a state begins and finishes. These are where the state sets up all the details it needs to start with and cleans up or prepares resources for the next state. Meanwhile, handleInput is invoked every time an input is delivered to the state and returns State? indicating that the result can be either another state or an empty value. If it is an empty value, the current one is still responsible for incoming events. Furthermore, the parame- ter object in interface’s methods represents an entity whose behavior the states control. Finally, all implementation of this interface should provide unique identification so that the state machine can differentiate among them by overriding the field id.

4.4.2 State Classes

State classes are fulfilment of above interface where specific behaviors are performed. Although each state can orchestrate multiple aspects of the object, it is recommended to commit to a single feature.

33

class DisplayTasksState: State { override val id = “DisplayTasksState”

var loading: Boolean = false private set

var tasksList: List = emptyList() private set

fun onEnter(Object object) { if (tasksList.isEmpty()) { loading = true tasksList = object.getTasks() loading = false } }

fun handleInput(Object object, input Input): State? { if (input is TaskSelectionInput) { val hasUndoneTask = tasksList.any { task -> !task.isDone } if (hasUndoneTask) { // Return an error state with current state id and the // error itself. return ErrorState(id, TaskNotDoneException()) } else { return DisplaySingleTaskState(input.getSelectedTaskId()) } } else { return null } }

fun onExit(Object object) {} }

Listing 6. An implementation called DisplayTasksState to display all tasks of a warehouse pro- cess

In warehouse application, viewing a list of tasks is usually one of the steps in users’ workflow. As a result, above code lines describe a simple state called DisplayTasksState to manage this behavior. From the beginning, an id is provided the same as the class’s name along with another two variables, which are loading and tasksList. The state will then try to request the object for those tasks it needs if they have not existed yet and then waits for TaskSelectionInput. When the input is received, the state checks whether there is an ongoing task. If the case is such, an error state is returned, otherwise a next state to display the selected task is expected.

34

4.4.3 Delegation to States class Object { lateinit var currentState: State

fun handleInput(input: Input) { State nextState = currentState.handleInput(this, input) if (nextState != null) { currentState.onExit(this) currentState = nextState currentState.onEnter(this) } } }

Listing 7. A sample demonstrates how inputs are delegated to a state

Above example is an Object class whose instances have a variable called currentState that tracks which state they are in. When the class’s instance receives an input from the user, it will let the state handle the rest and call appropriate methods when state is up- dated.

4.4.4 Summary

State machine is an excellent modeling pattern to overcome specific kind of situations. That is, for example, when (i) an entity, who is responsible for a series of input, changes it’s behaviors based on some internal states, and (ii) the state can be divided into a small number of distinct options.10 Undoubtedly, warehouse processes are such applicable models that can be broken down into small steps and each step can be treated as a state. The process will move forward as the user input correct information in each state and finish when the user confirm a task as completed. Since each behavior is handled independently, the application is less error-prone and suffer from less bugs.

4.5 Domain Specific Language (DSL)

Domain Specific Language (DSL) is invented with a main purpose to fill the gap between software and business development. With declarative manner in its nature, the language can allow people from management side to easily understand how the system works and then discuss related matters effectively with developers. Although DSL has been around

35 in software development for a while, the term has not been presented with a solid defini- tion yet. The subject will commence by explaining the term, followed by inspecting how it is applicable to Kotlin.

4.5.1 Definition

Martin Fowler11, in his book “Domain Specific Language”, has defined it as a language in that has a limited expressiveness focusing on a peculiar do- main. For a language to be considered as a proper DSL, it is required to have four fun- damental characteristics:

 It is a computer programming language, which can be comprehended by human and executed by a machine.11  It contains the nature of a language, which is fluent in delivering meaning in both individual and composed expressions.11  It limits the expressiveness, which merely provides enough features to sup- port a particular domain.11 In other words, DSL is suggested to function in a small domain and solve appropriate problems in lieu of a whole software.

In order to avoid confusion among different DSLs, the author divided this kind of lan- guage into three main categories, including external DSLs, internal DSLs and language benchmark. External DSL is independent of the primary language used by the software and can either use a custom syntax or another language’s syntax.11 For instance, one has probably encountered some examples of external DSLs such as Structured Query Language (SQL) in relational database management system or Extensible Markup Lan- guage (XML) in Hibernate’s configuration file.

On the other hand, internal DSL is a part of a general-purpose language which is likewise the software’s main language.11 However, despite the similarity, internal DSL script adopt only a subset features of the host language to serve a certain domain in a sense of a custom language.11 Some of the most popular general-purpose languages used to write DSL are Lisp, Ruby and Kotlin.

Finally, apart from mentioned DSLs, language workbench is a tool specially designed for defining and building DSLs within an IDE, where user is able to create new language at

36 will and integrate them together. A language workbench ensures three vital conditions to produce DSLs: a Semantic Model schema, which represents the subjects DSL de- scribes, a DSL editing environment, which manipulates the DSL scripts, and a Semantic Model behavior, which demonstrates what DSL script does by forming a Semantic Model with code generation.11 Meta Programming System from JetBrains is one of the DSL language workbenches known to date.

Figure 15. A state machine in MetaEdit language workbench11

4.5.2 Why DSL?

Sometimes, it is more straightforward to learn about a concept through examples. In order to understand the benefits that DSL brings out, two examples will be given: a con- ventional state machine’s setup and a Transport Layer Security (TLS) connection’s con- figuration.

37

4.5.2.1 Miss Grant’s Controller

This is an example from Martin11 about controlling Miss Grant’s secret compartment. The idea is gained from a gothic style security that in order to reveal an underground cham- ber, one needs to pull a lever on top of a stair. In this case, to open Miss Grant’s hidden area, Miss Grant must close the door, then open the second drawer in her chest and turn her bedside light on in either order:11

Figure 16. State diagram for Miss Grant’s compartment11

Above lays a state diagram construing a mechanism of a state machine:

 When the door is closed, the security system becomes active.  In Active state, Miss Grant can either turns the light on or opens the drawer.  After doing both actions, the system enters UnlockedPanel state, which unlocks the panel and locks the door.  In UnlockedPanel state, Miss Grant can close the panel to go back to Idle state, which unlocks the door and locks the panel.

38

Skipping all the declaration details, this is how it looks like when converting the diagram into , which uses a sequence statements to change a program’s state: val doorClosed = Event("doorClosed", "D1CL") val drawerOpened = Event("drawerOpened", "D2OP") val lightOn = Event("lightOn", "L1ON") val doorOpened = Event("doorOpened", "D1OP") val panelClosed = Event("panelClosed", "PNCL") val unlockPanelCmd = Command("unlockPanel", "PNUL") val lockPanelCmd = Command("lockPanel", "PNLK") val lockDoorCmd = Command("lockDoor", "D1LK") val unlockDoorCmd = Command("unlockDoor", "D1UL") val idle = State("idle") val activeState = State("active") val waitingForLightState = State("waitingForLight") val waitingForDrawerState = State("waitingForDrawer") val unlockedPanelState = State("unlockedPanel") val machine = StateMachine(idle) idle.addTransition(doorClosed, activeState) idle.addAction(unlockDoorCmd) idle.addAction(lockPanelCmd) activeState.addTransition(drawerOpened, waitingForLightState) activeState.addTransition(lightOn, waitingForDrawerState) waitingForLightState.addTransition(lightOn, unlockedPanelState) waitingForDrawerState.addTransition(drawerOpened, unlockedPanelState) unlockedPanelState.addAction(unlockPanelCmd) unlockedPanelState.addAction(lockDoorCmd) unlockedPanelState.addTransition(panelClosed, idle) machine.addResetEvents(doorOpened)

Listing 8. The setup of preceding state machine in imperative programming

The code in the listing seems to be confusing at first, even for a programmer. If some domain expert, or technical manager, wants to interpret the context, there should be another way to enhance the readability.

Instead of prior coding style, a more interpretative syntax, XML for example, can be used to compose the configuration. XML is a meta-language whose main purpose is for constructing other languages by defining human-readable vocabularies. As mentioned before, it is a proper candidate for writing DSLs. Here is the state machine setup by XML:

39

Listing 9. State machine’s setup in XML11

A benefit from utilizing XML is that developers do not have to redeploy the package every time a new state machine created or an existing one modified as it is enough to ship the XML to be read when the machine starts up.11 Apart from technical interest, the later state machine’s setup is more comprehensible than the previous one. Instead of worrying about how the program should run, it gives a more declarative approach on what the program should do.

40

4.5.2.2 Transport Layer Security Connection

TLS is a cryptographic security protocol that enables two parties to identify and authen- ticate each other and communicate with confidentiality and data integrity.12 Java Secure Socket Extension (JSSE) is a library that contains the functionalities to establish such connections. However, it is quite complicated and verbose to config the connection with that library:

@Throws(IOException::class) fun connectSSL(host: String, port: Int, tlsConfiguration: TLSConfiguration) { val tlsVersion = tlsConfiguration.getProtocol() val keystore = tlsConfiguration.getKeystore() val trustStore = tlsConfiguration.getTruststore()

try { val ctx = SSLContext.getInstance(tlsVersion) var tm: Array? = null var km: Array? = null

if (trustStore != null) { tm = getTrustManagers( trustStore.getFilename(), trustStore.getPassword().toCharArray(), trustStore.getStoretype(), trustStore.getAlgorithm() ) }

if (keystore != null) { km = createKeyManagers( keystore.getFilename(), keystore.getPassword(), keystore.getStoretype(), keystore.getAlgorithm() ) }

ctx.init(km, tm, SecureRandom())

val sslSocketFactory = ctx.getSocketFactory() val sslSocket = sslSocketFactory.createSocket( host, port ) as SSLSocket

sslSocket.startHandshake() } catch (e: Exception) { throw IllegalStateException("Not working :-(", e) } }

Listing 10. TLS connection configured by JSSE in traditional programming13

41

Similar to Miss Grant’s Controller example, the code is too specialized to be grasped by a person who is not a security expertise. There is a handful of details to be aware of in order to follow the context. The issue can be solved by using a Kotlin’s internal DSL: val fac = socketFactory { keyManager { open("certsandstores/clientkeystore") withPass "123456" beingA "jks" }

trustManager { open("certsandstores/myTruststore") withPass "123456" beingA "jks" }

sockets { cipherSuites = listOf( "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" ) timeout = 10_000 } } val socket = fac.createSocket("192.168.3.200", 9443)

Listing 11. TLS connection configured by JSSE by Kotlin’s internal DSL13

Although a large amount of code has been hidden regarding creation of the DSL, the later sample is much more obvious and straightforward. Therefore, configuration for TLS turns into a simple setting rather than pile of statements.

4.5.3 DSL In Kotlin

After the introduction to DSL, this section is meant for describing a type of DSL, internal DSL, using a relatively new language called Kotlin, which was introduced before. The initial step of the section is to unveil some crucial features that make the language suitable for building DSL. Later, a Kotlin sample is shown to illustrate how Kotlin DSL looks like in action after applying the cited features together.

42

4.5.3.1 Functions and Function Types

In a programming language, function is a reusable block of code that is used to perform a task or action. A function usually consists of a return type, a function name, a list of parameters and a function body: fun double(x: Int): Int { return 2 * x }

Listing 12. A function in Kotlin

Since the topic are relative broad, following sections particularly pay attention to special function related concept that help writing DSL in Kotlin possible.

4.5.3.1.1 Infix Notation

In Kotlin, it is viable to invoke a function without the dot and parentheses to improve the readability of the code:

// Shifts this value left by the x number of bits. infix fun Int.shl(x: Int): Int { ... }

// calling the function using the infix notation 1 shl 2

// is the same as 1.shl(2)

Listing 13. Infix notation in Kotlin2

Such feature in Kotlin is called the infix notion and is wielded by marking functions with infix keyword.

4.5.3.1.2 Higher-Order Functions

A high-order function is a function that either takes other functions as parameters or returns a function2, which can be illustrated as a pseudo code below:

43

function highOrderFunc(funcA) { funcA() return funcB }

Listing 14. A pseudo code showing a high-order function take a function as parameter and return another function as a result.

One well-known example is the idiom fold in functional programming for a collection, which accepts an initial accumulator value and a combining function to generate a return value.2 Specifically, the return value is built by recursively combining the current accu- mulator value with each collection element while the result of previous combination is assigned to the accumulator for the next process: fun Collection.fold( initial: , combine: (acc: R, nextElement: T) -> R ): R { var accumulator: R = initial for (element: T in this) { accumulator = combine(accumulator, element) } return accumulator }

Listing 15. Fold function in Kotlin

In the code above, combine is a function type that expects two arguments of types R and T and return a value of type R.

4.5.3.1.3 Function types

In order to pass a function into another function, Kotlin has introduced a concept of func- tion types. To declare a function type, it starts with function’s parameter types in paren- theses, followed by an arrow and a return type of the function:

Figure 17. Structure of function type in Kotlin3

44

In Figure 17, Unit type denotes a value returned by the function that has no meaning value. In a regular function declaration, Unit return type can be omitted. However, in a function type declaration, Unit must be there because the context demands a return type to be defined explicitly.

Besides, function types can optionally accept a receiver type, which can be expressed in below notation:

A.(B) -> C

Listing 16. Kotlin’s function type with receiver type as parameter

Igor Wojda14, author of Android Development with Kotlin, has defined receiver as the object whose methods are invoked. Therefore, the notation represents a function that can be called on a receiver object of type A with a parameter of type B and return a value of type C. Let’s look at how it works in Kotlin with a sample code: fun main() { val funcWithReceiver: Int.(Int) -> Int = { other -> plus(other) // or this.plus(other) }

var result = 0

result = 1.funcWithReceiver(2) println(result) // 3

result = funcWithReceiver(1, 2) println(result) // 3 }

Listing 17. Function type’s invocation with receiver type as parameter

The code begins with a funcWithReceiver variable whose type is a function type that has an Integer receiver, which inturn expect another Integer and finally produce a result with the same type. Then, the function can be either triggered from an integer itsef or invoked as a normal method. Another valuable point to notice in Listing 17 is a lambda, or function literal, used in order to declare the variable. The receiver object passed to the call, the Integer instance in this case, becomes an implicit this within the body of the function literal so that all its members are accesible without any additional qualifiers.2 The conception is called function literals with receiver, which is extremely useful when building DSL.

45

4.5.3.2 Lambdas

Lambda expressions, or lambdas, are blocks of code that can be passed as argument to a function call. In other words, they are so-called “function literals” that are not declared but passed immediately as an expression.2 val sum = { x: Int, y: Int -> x + y }

Listing 18. A full syntactic form of lambda in Kotlin

According to syntax in Listing 18, a lambda expression is surrounded by curly braces, where parameters, which is possibly separated by commas, goes first and the body goes later after an arrow.

In coding perspective, lambdas offer two significant benefits: closures and coherent syn- tax. Closure is a combination of a function and the lexical environment where that func- tion was declared.15 Consider this example: fun makeFunc(): () -> Unit { val name = "World" return { println("Hello, $name") } } fun main() { val myFunc = makeFunc() myFunc() // Hello, World }

Listing 19. A closure example written in Kotlin

In the example, makeFunc is a normal function returning a function type of () -> Unit that simply print out a sentence. After makeFunc was called, one would expect the variable name is no longer accessible after the function finished its execution. Nonetheless, the code still runs and myFunc gave “Hello, World” as a result. This is what called closure that has been depicted before. The lexical environment here is the environment compris- ing all local variables that were in scope at the time the function was created. In the end, closure is a convenient way for an inner function to access its outer function scope.

46

In Android development, here is a code sample that is usually written when the applica- tion wishes to listen to a button click: button.setOnClickListener(object: View.OnClickListener { override fun onClick(v: View?) { // Do something } })

Listing 20. Setting a listener for button’s click event

Listing 20’s code may seem ordinary at first, but the verbosity will become irritating when one has to write it repeatedly. Instead, it would be nice if developers only need to specify a shorter version: button.setOnClickListener { view -> // Do something }

Listing 21. The use of lambda to setup the listener.

Therefore, with lambda, the same thing is done in a more readable and cleaner approach. Previously, it is impossible to write such code in Android unless the project uses a library called Retrolambda16, since the platform solely support Java 6. However, as Google17 has announced Kotlin is now the second official language, it is feasible to bring the favorable syntax into development.

4.5.3.3 Person Builder DSL

From knowledge gathered from earlier sections, let’s defined a simple DSL to create an object of type Person: data class Person(val firstName: String, val lastName: String, val age: Int, val sex: String)

Listing 22. A simple class to hold all data related to a person

In order to collect person’s data at ease, a builder is constructed and contains some infix methods to enhance the legibility:

47

class PersonBuilder { var firstName: String = “” var lastName: String = “” var age: Int = 0 var sex = “”

infix fun firstName(value: String) = apply { firstName = value }

infix fun lastName(value: String) = apply { lastName = value }

infix fun age(value: Int) = apply { age = value }

infix fun sex(value: String) = apply { sex = value }

fun build(): Person = Person(firstName, lastName, age, sex) }

Listing 23. A builder to accumulate data for a person object

Lastly, a function is declared to initialization the builder and use it to spawn a person instance with given values: fun person(init: PersonBuilder.() -> Unit): Person { val builder = PersonBuilder() builder.init() return builder.build() }

Listing 24. The actual function to setup data and return a person instance

Assembling all the code to together, a good-looking DSL is determined to configure a person whose name is John Doe. val someone = person { this firstName "John" this lastName "Doe" this age 30 this sex "male” }

Listing 25. Final internal DSL form to configure a Person object

Although above case is trivial, it is merely a perception of how DSLs can be accomplished by utilizing Kotlin.

48

4.5.4 Summary

Generally, DSL is an excellent tool to improve development productivity, notably in object configuration. The language offer an apparent way to deliver an intent of a certain part in the infrastructure so that developers can fix mistakes and modify the system easier. Although DSL has its downsides, like building and maintenance cost, it is worth a try. However, there is one critical point to keep in mind: do not try to design DSL to look like a natural language. As DSL is still a programming language, it should feel like one, along with the terseness and precision compared to a natural language.4

4.6 Reactive Extensions (ReactiveX)

Reactive Extensions, or ReactiveX in short, is a library meant for composing asychronous and event-based program by combining the best ideas from Observer pattern, pattern, and functional programming18. In other words, it grants developers the ablility to apply a heterogenous pool of operators on sequences of data or events while abstracting away low level concerns like threading, thread-safety, synchronization, etc. However, the librabry is sometimes confused with Functional Reactive Programming (FRP) invented by Conal19. In spite of being close to functional and reactive, authors of both inventions has claimed that the notions are not the same. The main distinction is believed to be the fact that FRP acts on values that continously change over time while ReactiveX operates on discrete values which are emitted over time.18 To understand the potental advantages, let’s take a look at the core concepts of the library.

4.6.1 Observer Pattern and Reactive Behavior

Observer pattern is one of the most popular Gang of Four design patterns defining one- to-many dependency between objecs so that if one object changes, all of its dependants are notified and updated accordingly.20 In particular, the pattern describes the relationship between two keys objects: subject and observer. A subject may have any number of observers and all of them will be notified when the subject undergoes a change in state. This is the chance for the observers to synchronize and react to the update of the subject. Besides, the subject does not have to know who the observers are

49 and that observers are also completely unknown to each other, which results in decoupling and resuable code.

ReactiveX has adopted the pattern in form of Observable and Observer. The Observable is either a finite or infinite stream that push values whenever they are available while the Observer registers to the Observable that it is interested in and pick up those emitted items. Compare to the ”pull” nature of Iterable, Observable follow the ”push” approach”, as illustrated in the table below:

Event Iterable (pull) Observable (push)

Retrieve data T next() onNext(T)

Encounter error throws Exception onError(Exception)

Complete !hasNext() onCompleted()

Table 2. Comparison between synchronous/pull Iterable and asynchronous/push Observable21

In Table 2, the Observable type improves the current observer pattern by adding two missing semantics:

 onComplete, which is the ability to notify the Observer that the Observabe has no more data coming to deliver.21  onError, which is the ability to notify the Observer that the Observable has run into an error.21

To a certain extent, observer pattern gives the program a reactive feeling where one object is waiting for another object in order to response to a result. The listening object does not know when it will obtain an item, but it knows what to do when that happens.

50

The demeanor is crucial in concurrent programming, which will be described in further detail in upcoming sections.

4.6.2 Functional Programming and Composable Behavior

As specified when introducing Kotlin, functional programming is a programming paradigm that is built around mathematical functions instead of step-to-step statements to express the ideas. ReactiveX is not meant for implementing the whole paradigm but rather be functional by borrowing some of the useful ideas. Hence, not only are items emitted by an Observable immutable but also mathematical functions can be applied during the emission to generate the ancipated result:

Observable.just(1, 2, 3, 4) .filter { num -> (num % 2) == 0 } .map { num -> num * 2 } .subscribe { num -> println(num) } // 4, 8

Listing 26. Integers emitted by an Observable are filtered and transformed to a new value

Above code sample demonstrates an Observable whose values are filtered out if they are not an even number and then multiplied by two, ending up with four and eight. In fact, no matter how large the amount is, Observer can always expect results of even numbers whose value is double compared to the original one. Therefore, even in larger scale, one thousand numbers for example, the outcome is identical. To promote func- tional programming, the library offers both a considerable amount of ready-made func- tions and other means to write custom ones.

Additionally, ReactiveX are intended for composing flows and sequences of asynchronous data18, and thus offers the flexibility to assemble many different streams: val obsA = Observable.just(1, 2, 3, 4) val obsB = Observable.just(5, 6, 7, 8) obsA.concat(obsB) .subscribe { num -> println(num) } // 1, 2, 3, 4, 5, 6, 7, 8

Listing 27. Concatenation of two integer streams to produce a new one

51

In prior listing, two integer sequences are combined to create a new flow, which emits the items in the order that previous ones associated with each other. Consequently, this opens several opportunities in programming since developers can incorporate various functionalities to achieve another one without going through complexity and verbosity.

4.6.3 Asynchronousity and Threading in Android

Thread, within the proramming world, is a path of code execution through a program and each thread has its own local variables, program counter and lifetime.22 During an application development, working with concurrent events is invetable when one has to deal with multiple threads, especially in Android. In such platform, all code is executed on a default thread called main thread or UI thread, including continuos operations to draw elements on the screen. If the application makes a network call that can take a few seconds, the screen can freeze for some time because the UI code needs to concede the main thread to network code. The common way to prevent this kind of problem is to have a separate thread take care of network requests and return to the main one after the requests are done.

4.6.3.1 AsyncTask

One of the most prevalent approaches in Android to coop with asynchronous units is AsyncTask. Here is an example of how an AsyncTask is constructed:

52

class MainActivity: AppCompatActivity() { …

fun init() { val task = DownloadFilesTask<…> { totalSize -> showDialog(“Downloaded $totalSize bytes”) } task.execute() } } class DownloadFilesTask: AsyncTask( private val onFileDownloaded: (Long) -> Unit ) { protected fun doInBackground(vararg urls: URL): Long { var count = urls.length var totalSize = 0; for (i in 0 until count) { totalSize += Downloader.downloadFile(urls[i]) publishProgress((i / (count.toFloat()) * 100).toInt()) if (isCancelled) { break } } return totalSize }

protected fun onProgressUpdate(vararg progress: Int) { setProgressPercent(progress[0]) }

protected fun onPostExecute(result: Long) { onFileDownloaded(result) } }

Listing 28. An AsyncTask that is responsible for downloading a file23

Within an AsyncTask object, there are four fundamental methods, which are onPreExe- cute, doInBackground, onProgressUpdate and onPostExecute. As the names infer, on- PreExecute, onProgressUpdate, onPostExecute are called, on the main thread, before, during and after the task is executed while doInBackground is the actual logic runner, which is served on an isolated background thread. In the example, the task starts to download a file in background while continuously publish the progress in percentage. Then, when the file is loaded, the task will show a dialog to user to announce that the job has finished.

The Android component works efficiently for such elementary demand. Nonetheless, as the requirements evolve, chaining operations for example, so does the implementation’s complexity. In addition, the declaration of AsyncTask introduces lots of boilerplate that reduce the readability of the code.

53

class MainActivity: AppCompatActivity() { …

fun init() { val taskA = ChainedTask<…>(null) { doTaskA() } val taskB = ChainedTask<…>(taskA) { doTaskB() } val taskC = ChainedTask<…>(taskB) { doTaskC() } taskC.execute() } } class ChainedTask<…> constructor ( private val anotherAsyncTask: AsyncTask<…>?, private val codeToRun: () -> Unit ): AsyncTask<…>() { …

protected fun doInBackground(vararg urls: URL): Long { codeToRun() }

protected fun onPostExecute(result: Long) { anotherAsyncTask?.execute() } }

Listing 29. Chaining with AsyncTask

Sample code in Listing 29 becomes complicated when there is a need to invoke another task after one task is done, which introduces a callback hell, a symptom that consists of multiple nested callback, or task in this case, that makes code difficult to read and debug.

4.6.3.2 Coroutine

A coroutine is a popular concurrency design pattern that can be used to simplify code that executes asynchronously.24 The concept has been implemented in many languages and recently added to Kotlin version 1.3.24 Consequently, Android developers can take advantage of it to coop with two primary problems:

 Long-running tasks that might slow or freeze the app  Network and disk operations that can block the main thread.

In general, coroutines are no different than threads. They are thread’s light-weight ver- sion with the ability to suspend and resume their own execution.

With coroutines, above file downloading sample by AsyncTask can be changed to a more synchronous intuition:

54

class MainActivity: AppCompatActivity() { …

fun init() { GlobalScope.launch(Dispatcher.MAIN) { val totalSize = downloadFiles(urls) showDialog(“Downloaded $totalSize bytes”) } }

suspend fun downloadFiles(vararg urls: URL) = withContext(Dispatcher.IO){ var count = urls.length var totalSize = 0; for (i in 0 until count) { totalSize += Downloader.downloadFile(urls[i]) } totalSize // return total size (return keyword is not required) } }

Listing 30. Downloading file using Kotlin’s coroutine

Coroutines build upon regular functions by adding suspend and resume keyword:

 suspend pauses the execution of current coroutine and saves all local variables24  resume continunes the execution of a suspended coroutine from the place it was suspended.24

Essentially, suspend functions can only be invoked from another suspend functions or a coroutine, which can be started by a coroutine builder. In Listing 30, downloadFiles is a suspend function that downloads files based on given urls and return the total downloaded size, while being executed in a IO thread. The function is triggered inside a coroutine built by a coroutine builder called launch in context of a coroutine scope named GlobalScope, which indicates the lifetime of created coroutine is limited to by the lifetime of the application. Here inisde the coroutine builder lays the most advantageous part of coroutine. First, the coroutine is launched in the main thread, specified by MAIN dispatcher. Next, it pauses the execution to invoke downloadFiles function. After the function finishes, the coroutine resumes, assigns the result to totalSize variable, and shown the information to user in form of a dialog. Instead of worrying about putting the logic the callback, the implementation provide a natural programming feeling where statement runs after statement. As a result, complex logic like chaining operations requires less amount of code:

55

class MainActivity: AppCompatActivity() { …

fun init() { GlobalScope.launch(Dispatcher.MAIN) { taskA() taskB() taskC() } }

suspend fun taskA() = withContext(Dispatcher.IO){ // Do task A }

suspend fun taskB() = withContext(Dispatcher.IO){ // Do task B }

suspend fun taskC() = withContext(Dispatcher.IO){ // Do task C } }

Listing 31. Successive operations implemented by using coroutines

4.6.3.3 RxJava

Another solution for asynchronous programming in Android is RxJava, an implementa- tion of ReactiveX in Java which was described above. Like coroutines, Observable clas- ses in the library helps the code be more transparent since the chaining and invocation are performed in one place: val taskA = Observable.create { … } val taskB = Observable.create { … } val taskC = Observable.create { … } class MainActivity: AppCompatActivity() { …

fun init() { taskA.concat(taskB).concat(taskC).subscribe { // do something } } }

Listing 32. Chaining with Observable

56

Another crucial competence that the Observable also possesses is the capability of switching between threads. Together with pre-defined Schedulers, it is feasible to swap thread even at the call site: fetchItemObservable.subscribeOn(Schedulers.io) .observeOn(Schedulers.computation) .map { item -> // do some computing } .observeOn(AndroidSchedulers.mainThread) .subscribe { item -> // display to user }

Listing 33. Switching threads in RxJava, an implementation of ReactiveX

Listing 33 illustrates an Observable that first fetches an item on an input/output (IO) thread, then does some computation based on the result on a computational thread, and finally displays the processed data on the UI thread. The items emitted in succession are ensured to be thread-safe so that the data is what one would anticipate. Clearly, the library allows the code to run in an optimized thread without dealing concurrency prob- lems.

4.6.3.4 AsyncTask, Coroutine, and ReactiveX Comparison

All three asynchronous techniques have handed over their own way to run blocking op- erations outside of the main thread to create a smoother experience for users. Even though there quite many differences between them, programming style is the most sig- nificant characteristic that makes the clear distinction.

For developers who are familiar with callback style, AsyncTask or ReactiveX is more suitable for them. People following this path should always be careful about where to the logic. The most common mistake happens while programming in this style is assuming a value is available when the background work has not finished:

57

fun main() { var result = 0 getNumberOfTasks() .subscribeOn(Schedulers.io) .observeOn(AndroidSchedulers.mainThread) .subscribe { it -> result = it } println(“Number of tasks are $result”) // Number of tasks are 0 }

Listing 34. Assumption mistake while doing callback style

In the preceding listing, result variable is assumed to exist after the RxJava subscription. However, since getNumberOfTasks method runs in background, there is no clue when the response arrives. Meanwhile, as the next line is executed immediately, the message does not include the anticipated content. The only solution in this case is to put the print- ing code in subscribe callback.

For developers who is more comfortable with consecutive execution style, coroutines is obviously a better choice. Because statement run after statement, the execution order always occurs as expected. Again, with previous sample, here is how it looks like by using coroutines: fun main() { GlobalScopt.launch(Dispatcher.MAIN) { var result = 0 result = getNumberOfTasks() // method returns 5 println(“Number of tasks are $result”) // Number of tasks are 5 }

}

Listing 35. Code runs from top to bottom order when using coroutine

Thanks to the sequential execution order, the code is easier to read and understand.

Other than the programming style, coroutines also provide a native implementation that support resources’ optimization and allow applications to be independent of external libraries. However, in consideration of Kotlin still being a new learning curve for most of developers in the company to grasp, ReactiveX was selected as the final solution for the multimodal framework.

58

4.6.4 Summary

ReactiveX is a valuable concept that solve serveral issues in programming and enhance the code quality. For the team’s interest, it indeed yield a better implementation than AsyncTask and a more convenient tool than coroutines at the time of development, although there is a risk that the final product is heaviy attached to the library. When the framework become stable, there will be improvement tasks to apply coroutines gradually.

5 Implementation

5.1 Overview

Written in Kotlin, the Android multimodal framework is a solution for developing applications on multiple Android based platform that use Flux architecture along with other aforementioned patterns and practices. In spite of Flux being the skeleton of the project, the terms and usage are adjusted slightly depending on the needs. Here is the simplfied graph depicting the architecture:

59

Figure 18. A graph representing the general idea of the framework

As described in Figure 18, the implementation contains support for two different types of interaction: a classic screen display and a voice interface. While these are separate functionalities under end user’s persepective, they are both treated as views. Below is a list of responsibilites that these view components would do:

 They read data and receive updates via the state machine to display necessary information.  They send inputs or actions as user interacts to the state machine to be processed.

In this chapter, vital elements constructing the framework are discussed in more detail and the last section is dedicated to analyzing the pros and cons in the resultant implementation.

5.2 State Input

State inputs represent the actions in Flux architecture that are created according to user interaction, which are either screen touches or voice instructions. In fact, the StateInput

60 class is merely an interface and the application can implement and have as many concrete inputs as it needs: interface StateInput data class ScanInput(val input: String): StateInput data class EntitySelectionInput(val selectedEntity: T): StateInput

Listing 36. Input classes derived from the common StateInput interface

Inherited by the idea of Flux, the inputs are the only mean to trigger a state’s change in the system. However, if the input is invalid or contradict the business logic, an error is shown to user and no modification is made.

In Kotlin, there is a type of class called data class, whose main purpose is to hold data. These classes fit with the intent of the state inputs and thus are perfect candidate to represent them. With data classes, some standard funcationality and utility functions are often derivable from the data itself such as equals(), hashCode(), toString(), copy(), etc.

5.3 State Machine

State machine, or dispatcher in Flux context, is the central hub where inputs are received and orchestrated to the current state to be interpreted. Each state machine represents a process in warehouse that can only be in one state in a list of predefined states. Like the dispatcher, the machine has no knowledge of actual business logic. It must take care of the transitioning between states and to provide access to other resources such as data sources and Android context.

Since the goal is to simplify the logic and to provide a complete picture of the warehouse’s procedure, state machines are configured using DSL:

61

val processStateMachine = stateMachine { startingState = “state1”

states { state1 { id = “state1” onFinishedTransitionTo = “state2” }

state2 { id = “state2” onFinishedTransitionTo = ”state3” }

state3 { id = “state3” isItemSelectable = true, onItemSelectedTransitionTo = “state4” onItemCompletedTransitionTo = “state5” }

… } }

Listing 37. Setup of state machine for dummy process

From Listing 37, it is obvious to understand how the process behaves. Depending on which action user takes, the state machine moves to the next appropriate state. This kind of declaration is useful not only for developers but also for domain experts so that they can verify whether the app should work as expected.

5.4 States

States are the brain of the whole operation. It is in charge of the entire business domain, from processing user’s inputs to giving instructions to the the state machine on how to proceed. All interaction with the user at the front is totally controlled by these states. Therefore, if there is a bug in a view that allow user to mark a task as done, the data is still intact because it is an invalid action to the state. In other words, this behavior imposes strict rules to the infrastructure in a data-driven way.

Adhere to state pattern, the State class has one main method to process inputs passed from the state machine:

62

fun processInput(input: StateInput, processContext: ProcessContext) { when (input) { is ScanInput -> // do something is EntitySelectionInput<*> -> // do something else } }

Listing 38. An example of input handling in state when expression is a control flow in Kotlin replacing switch operation in C-like languages. Here, the state can decide with input it is interested in and act respectively. The process context in the method is the access point to data sources of the app. If some input is supposed to change to underlying data, the state will use the parameter to make adjust- ment. One missing factor in the method is the return value to indicate whether the pro- cess should move to the next state. At the time when the thesis is being written, there are still major ongoing refactorings around the state, which one of them is related to the indication. Nonetheless, there should be a variable inside the state or a method output to resolve the matter in the end.

5.5 Views and Voice

Views and voice in the solution are considered extrinsic components and do not actually belong to the solution. This is the anticipated demeanor because the framework do not care what client it serves. For normal screen interactions, there is no requirement for any complicated communication so client app can use activities and fragments from Android as the view controllers to receive inputs and send them to the state machine. For voice interactions, the framework offers voice interfaces that can be implemented on demand and managed by a voice controller. The voice interfaces create flexibility that allows the consumer app to switch between third party voice libraries at ease.

5.5.1 State Fragments

In Android, views are the View objects that is populated by an XML layout and controlled by activities and fragments. In the project, fragments is heavily used instead of activities because of it’s reusability. For example, a VerifyProductFragment, which verifies the validity of a product in the warehouse, can be utilized in different processes including

63 unloading, picking, transferring, etc. Furthermore, a fragment can associate with either one or multiple states one at a time.

5.5.2 Voice Interfaces

Since there are quite many promising solutions for voice handling on the market, it was decided to abstract those particulars away from the plan instead of embedded one of them into the framework. As a result, there are two interfaces for voice that is worth noting: a voice recognition interface, which is responsible for listening to user’s commands, and a text-to-speech synthesizer, which is accountable for speaking messages out loud. The interfaces are regulated by a voice controller, which obliges the architecture with all voice related concerns. interface VoiceRecognitionInterface { val onCommandReceived: PublishSubject

fun startListening(recognitionType: VoiceRecognitionType)

fun stopListening() } interface TextToSpeechSynthesizer { // = speak text fun synthesizeSpeech(speech: String) }

Listing 39. Declaration of voice recognition and text-to-speech synthesizer interfaces

According to Listing 39, the declaration of the interfaces is straightforward. No matter what voice library is chosen, the implementation should start listening to the recognition type specified as the parameter in the method. The recognition type can be a keyword or a series of symbols. When a voice result is recognized, it is mapped to command and emitted through onCommandReceived event. Consequently, the voice controller re- ceives and processes the command and pass the final input to the state machine. When- ever the state machine has an instruction or an error notification to deliver to user, the voice controller will use the text-to-speech synthesizer to speak the message. The actual synthesizer execution can use either an independent or a device’s native synthesizer to carry out the transmission.

64

5.6 Summary

The implementation based on Flux architecture as well as other technologies and patterns has produced an outcome that was successful in achieving separation of concerns, a result which the developers has been striving for a long time. With the principle, the software has already achieved many different goals:

 Business logic, like VerifyProductSerialNumber or VerifyProductLocation, was decoupled completely from other interestes. In fact, UI elements, such as text areas and buttons, or communication between components, like state transitions, are the least things to worry about when starting to develop an application. It does not mean that such matters are not important in software development but they are the factors that most likely to be changed throughout the time.  Instead of modifying only a part of MVC, which is the architecture that has been used in most of the company products, it is better to replace the current one with a brand new approach that focuses on the business logic first before considering any framework or tool. MV* pattern in Android has been involving rapidly from MVC to MVP and MVVM. Although there are distinct differences among those patterns, it depends on the personal taste and the necessities when it comes to adoption as some people might feel improvements in MVVM and MVP towards MVC are not worth to commence a change.  Because of the segregation in the code, it is now more maintainable. Every component in the framework has a single responsibility. Thus, when there are needs for logic modification or bug fixes, it is straightfoward to identify the place to make the adjustment. For instance, if a task can be mark as done with a default location when no location is specified, states are the areas one would like to investigate at the beginning.  Beside the maintainability, the testability of the code has also increased. Indeed, business logic can be tested easily by using simple unit tests, where UI can be tested separately using other special test frameworks. This is an important aspect because UI tests sometimes require an emulator or a real device in order to carry out the tests, while testing pure logic does not have anything to do with the external resources.  Mutimodalities, including normal display and voice, are supported as distinct plugins to the framework. Since the core state module that is in charge of logic management does not care about who are the consumers, it does not matter whether the environment is a phone operated by touch interaction or a smart glasses controlled by voice commands.

On the other hand, the product still have some drawbacks while persuing the ideas:

65

 Despite the high separation of concern in design, the framework is tied tightly to the Android platform. As a result, if the company wish to move to another platform like iOS or desktop, there is a chance that the framework has to be redone. In the past, many applications were started over from scratch when the company decided to change from old mobile phone system to Android devices.  In some degree, the implementation of the framework remains complex and it requires a new learning curve for developers who would love to try out. Concepts like ReactiveX or Kotlin are still new to the organization and thus will cause some hiccups along the way.

Nevertheless, the issues mentioned above are not so serious that the company has to worry about. Regarding to platform, Android is enjoying the period of prosperity and thus not likely to be replaced in near future. Besides, relating to complexity and knowledge barricade, extensive refactoring is being carried out in order to simplify the code and hide some implementation detail. For example, to do an asynchronous call, developer can return a special object that knows how to execute to logic and the rest will be handled by the framework itself, eithy by using ReactiveX or Kotlin’s coroutine. In the end, from experimental tasks, the project has been approved to be in official development and become key strategy in the future.

6 Conclusion

The core idea of this final year study, including the Android multimodal framework and the POC, was quite successful in many aspects. As for personal interest, it was a great opportunity to improve programming skills as well as learn about new and promising technologies like voice and smart glasses, which not only aided the thesis but also would assist the future works. Besides, the author has gained a deep understanding about warehouse processes and how the mobile applications could be a pain reliever for the warehouse operators. In fact, there are differences between a mobile application in a warehouse used by a few persons and a mobile application in social media used by a million of people. Each application targets a particular group of users and hence focuses on distinct criteria. Therefore, the project gave the author a chance to accumulate knowledge to build better software in various circumstances. Finally, it was a great pleasure to contribute a valuable product towards the company’s growth and knew that it would bring a lof of benefits to many people.

66

References

1 JetBrains s.r.o. Kotlin: Statically typed programming language for the JVM, Android and the browser [Internet]. [place unknown]: JetBrains s.r.o; 2000 [updated 2019; cited 2019 Aug 7]. Available from: https://www.jetbrains.com/opensource/kotlin/.

2 Kotlin Foundation. Why Kotlin? [Internet]. [place unknown]: [publisher unknown]; 2017 [updated 2019; cited 2019 Aug 7]. Available from: https://kotlinlang.org/.

3 Isakova S, Jemerov D. Kotlin In Action. New York (NY): Manning Pubulications; 2017.

4 Multi-OS Engine. Multi-OS Engine: Create iOS Apps in Java [Internet]. [place unknown]: [publisher unknown]; 2017 [cited 2019 Aug 7]. Available from: https://multi-os-engine.org

5 Robert C. M. The Clean Code Blog [Internet]. [place unknown]: Robert C. Martin. 2011-. The Clean Architecture; 2012 Aug 13 [cited 2019 Sep 15]; [about 1 screen]. Available from: https://blog.cleancoder.com/uncle-bob/2012/08/13/the- clean-architecture.html

6 Stephen W. Stephen Walther: ASP.Net, Metro, and Html5 [Internet]. [place unknown]: Stephen Walther. 2019-. The Evolution of MVC; 2008 Aug 24 [cited 2019 Sep 15]; [about 1 screen]. Available from: http://stephenwalther.com/archive/2008/08/24/the-evolution-of-mvc

7 Trygve R. The original MVC reports [Internet]. Olso: Dept. of Informatics, University of Olso; 1979 May 12 [cited 2019 Sep 15]. 11p. Available from: http://heim.ifi.uio.no/~trygver/2007/MVC_Originals.pdf

8 Facebook, Inc. In-depth overview [Internet]. [place unknown]: Facebook Open Source; 2019 [updated 2019; cited 2019 Jul 6]. Available from: https://facebook.github.io/flux/docs/in-depth-overview

9 Boduch A. Flux Architecture. [place unknown]: Packt Publishing; 2016.

10 Nystrom R. State; Game Programming Pattern / Design Patterns Revisted [Internet]. [place unknown]: Genever Benning; 2014 Nov 2 [cited 2019 Jul 20]. Available from: https://gameprogrammingpatterns.com/state.html

11 Fowler M. Domain Specific Languages. United States of America: Addison- Wesley Professional; 2010.

67

12 IBM Corporation. Secure Sockets Layer (SSL) and Transport Layer Security (TLS) concepts [Internet]. [place unknown]: IBM knowledge center; 1999 [updated 2019 Apr 4; cited 2019 Aug 3]. Available from: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.do c/sy10640_.htm

13 Wirtz S. Kotlin Expertise Blog [Internet]. [place unknown]: Simon Wirtz. 2016-. Create a DSL in Kotlin; 2017 Aug 5 [cited 2019 Aug 4]; [about 1 screen]. Available from: https://kotlinexpertise.com/create-dsl-with-kotlin

14 Wojda I. Programmer dictionary: Receiver. 2017 Nov 23 [cited 2019 Aug 7]. In: Kt. Academy. Site with mission to simplify Kotlin learning [Internet]. [place unknown]: Medium. 2019-. [about 1 screen]. Available from: https://blog.kotlin- academy.com/programmer-dictionary-receiver-b085b1620890

15 Mozilla and individual contributors. Closures [Internet]. [place unknown]: [publisher unknown]; 2005 [updated 2019; cited 2019 Aug 7]. Available from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

16 Tatarka E. A gradle plugin for getting java lambda support in java 6, 7 and android [Internet]. [place unknown]: Github; 2013 [updated 2019; cited 2019 Aug 7]. Available from: https://github.com/evant/gradle-retrolambda

17 Google LLC. Develop Android apps with Kotlin [Internet]. [place unknown]: [publisher unknown]; 2019 [cited 2019 Aug 7]. Available from: https://developer.android.com/kotlin

18 ReactiveX. An API for asynchronous programming with observable streams [Internet]. [place unknown]: [publisher unknown]; 2019 [cited 2019 Aug 14]. Available from: http://reactivex.io

19 Elliot C. The essence and origins of FRP [Internet]. [place unknown]: Github; 2015 [updated 2019; cited 2019 Aug 15]. Available from: https://github.com/conal/talk-2015-essence-and-origins-of-frp

20 Helm R, Gamma E, Johnson R, Vlissides J. Design Patterns: Elements of Reusable Object-Oriented Software. United States of America: Addison-Wesley Professional; 1994.

21 Morgillo I. RxJava Essentials. [place unknown]: Packt Publishing; 2015.

22 Hyde P. Java Thread Programming. United States of America: Sams; 1999.

68

23 Google LLC. AsyncTask [Internet]. [place unknown]: [publisher unknown]; 2019 [cited 2019 Aug 7]. Available from: https://developer.android.com/reference/android/os/AsyncTask

24 Google LLC. Improve app performance with Kotlin coroutines [Internet]. [place unknown]: [publisher unknown]; 2019 [cited 2019 Oct 7]. Available from: https://developer.android.com/kotlin/coroutines#start

Appendix 1 1 (1)