Smart Queries: ask Pepper about monsters and get accurate replies

Basic setup of the application

In this section, we’ll focus on the necessary pure Android parts, namely, loading the monster data, and displaying a list of monsters on the tablet.

Start by the default to create an app:

This zip file contains the needed assets, namely:

  • A set of images, to place inside your app’s res/drawable folder
  • The monsters_data.json file, to place in your app’s res/raw folder (create if needed)

Loading the monster data

For doing this, we’ll use the third-party library “Klaxon” to parse the JSON file into Kotlin objects. Include this library by adding this to your app’s build.gradle file:

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'com.beust:klaxon:5.4'

Now we’ll define a “monster” object, and a function for loading it. Create a file in your application called MonsterData.kt:

import com.beust.klaxon.Klaxon
import java.io.File
import java.io.InputStream

data class Monster(
   val filename : String,
   val name : String,
   val color: String,
   val eyes: Int,
   val eyeColor: String,
   val horns: Int,
   val arms: Int,
   val tentacles: Int,
   val fur: Boolean,
   val wings: Int,
   var resourceId : Int = 0
)

fun readMonstersJSON(inputStream: InputStream): List<Monster> {
   return Klaxon().parseArray(inputStream) ?: listOf()
}

fun main() {
   val file = File("app/src/main/res/raw/monsters_data.json")
   val monsters = readMonstersJSON(file.inputStream())
   println("Blue monsters: ${monsters.filter {it.color=="blue"}.size}")
   println("Blue-eyed monsters: ${monsters.filter {it.eyeColor=="blue"}.size}")
}

This “main” function is just for testing, you can run it (with the little “play” icon) appearing in the margin to check that the data is loaded correctly.

Note that all the attributes of a “Monster” object are loaded from the JSON, except the resource ID, which we’ll set later.

Android activity

Make your main activity layout (res/layout/activity_main.xml) a horizontal, empty, LinearLayout, with center gravity:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/imageHolder"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center"
   android:orientation="horizontal"
   tools:context=".MainActivity" />

Now in MainActivity.kt, we can define our constants, and a helper function for showing monsters:



val TAG = "MainActivity"
private lateinit var imageHolder : LinearLayout
private val imageLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
       LinearLayout.LayoutParams.MATCH_PARENT, 1.0f)

fun showMonsters(monstersToShow: List<Monster>) {
   runOnUiThread {
       Log.i(TAG, "Showing ${monstersToShow.size} monsters")
       imageHolder.removeAllViews()
       monstersToShow.forEach { monster ->
           val imageView = ImageView(applicationContext).apply {
               id = View.generateViewId()
               layoutParams = imageLayoutParams
               setImageResource(monster.resourceId)
           }
           imageHolder.addView(imageView)
       }
   }
}

Now in our onCreate, you can

  1. Load the monster data
  2. Set the resourceId attribute on these

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
   imageHolder = findViewById(R.id.imageHolder)
   // loading of monster data
   val monsterData = resources.openRawResource(R.raw.monsters_data)
   val monsterList = readMonstersJSON(monsterData)
   monsterList.forEach {
      it.resourceId = resources.getIdentifier(it.filename, "drawable", packageName)
   }

   // Example: show blue-eyed monsters
   showMonsters(monsterList.filter {it.eyeColor == "blue"})
}

You can now run the application on Pepper (or an emulator), it should show a line of blue-eyed monsters on the tablet (you may need to select “app” in the run project drop-down menu).

Next: let’s make the line of shown monsters correspond to what the user asked for.