QiChatbot – Mastering Dynamic concept

Goal

In this tutorial, we will create a dynamic QiChatbot, using Dynamic concepts.

For further details, see: QiChat dynamic.

Prerequisites

Before stepping in this tutorial, you should:

Let’s start a new project

  • Start a new project, let’s call it DynamicConceptsPepper.
  • Robotify it and make sure it implements the QiSDK & the Robot Life Cycle.

For further details, see: Creating a robot application.

Creating the topic file

Create a new topic file named greetings_dynamic.top for the English (en) language.

Add the following content to this file:

topic: ~greetings_dynamic()

dynamic: greetings

u:(~greetings) ~greetings

For more details about the syntax, see: QiChat - Syntax.

Getting a dynamic concept

In this section, we will see how to retrieve the greetings dynamic concept present in our topic.

A dynamic concept is represented by an EditablePhraseSet. It contains a list of Phrase objects that constitute the elements related to the dynamic concept.

Store an EditablePhraseSet in your MainActivity:

// Store the greetings dynamic concept.
private var greetings: EditablePhraseSet? = null
// Store the greetings dynamic concept.
private EditablePhraseSet greetings;

To retrieve a dynamic concept, use the dynamicConcept method on a QiChatbot instance. It takes the dynamic concept name as parameter (here “greetings”).

In the onRobotFocusGained method, add the following code:

 // Create a topic.
val topic: Topic = TopicBuilder.with(qiContext)
        .withResource(R.raw.greetings_dynamic)
        .build()

// Create a new QiChatbot.
val qiChatbot: QiChatbot = QiChatbotBuilder.with(qiContext)
            .withTopic(topic)
            .build()

// Create a new Chat action.
val chat: Chat = ChatBuilder.with(qiContext)
            .withChatbot(qiChatbot)
            .build()

// Get the greetings dynamic concept.
greetings = qiChatbot.dynamicConcept("greetings")

// Run the Chat action asynchronously.
chat.async().run()
// Create a topic.
Topic topic = TopicBuilder.with(qiContext)
        .withResource(R.raw.greetings_dynamic)
        .build();

// Create a new QiChatbot.
QiChatbot qiChatbot = QiChatbotBuilder.with(qiContext)
            .withTopic(topic)
            .build();

// Create a new Chat action.
Chat chat = ChatBuilder.with(qiContext)
            .withChatbot(qiChatbot)
            .build();

// Get the greetings dynamic concept.
greetings = qiChatbot.dynamicConcept("greetings");

// Run the Chat action asynchronously.
chat.async().run();

Updating a dynamic concept

Add content

To add a Phrase to a dynamic concept, use the addPhrases method. Add the following code to your MainActivity class:

private fun addGreeting(greeting: String) {
    greetings?.async()?.addPhrases(Collections.singletonList(Phrase(greeting)))
}
private void addGreeting(final String greeting) {
    if (greetings != null) {
        greetings.async().addPhrases(Collections.singletonList(new Phrase(greeting)));
    }
}

Remove content

To remove a Phrase from a dynamic concept, use the removePhrases method. Add the following code to your MainActivity class:

private fun removeGreeting(greeting: String) {
    greetings?.async()?.removePhrases(Collections.singletonList(Phrase(greeting)))
}
private void removeGreeting(final String greeting) {
    if (greetings != null) {
        greetings.async().removePhrases(Collections.singletonList(new Phrase(greeting)));
    }
}

Testing the update

Let’s test our code using a list representing the greetings.

Modify your activity_main.xml file with the following code:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusableInTouchMode="true"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/greeting_editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="Greeting"
        android:inputType="text"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintRight_toLeftOf="@+id/add_button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/add_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Add"
        app:layout_constraintBottom_toBottomOf="@+id/greeting_editText"
        app:layout_constraintLeft_toRightOf="@+id/greeting_editText"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/greeting_editText" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:fadeScrollbars="false"
        android:scrollbarStyle="outsideInset"
        android:scrollbars="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/greeting_editText" />

</android.support.constraint.ConstraintLayout>

The dynamic concept content must represent the greetings of the list.

Add the following field to the MainActivity class:

private var greetingAdapter: GreetingAdapter? = null
private GreetingAdapter greetingAdapter;

And add the following code in the onCreate method:

// Create adapter for recycler view.
greetingAdapter = GreetingAdapter(this::removeGreeting)

// Setup recycler view.
recyclerView.layoutManager(LinearLayoutManager(this))
recyclerView.adapter(greetingAdapter)

// Add greeting on add button clicked.
add_button.setOnClickListener {
    val greeting: String = greeting_editText.text
    greetingEditText.text = ""
    // Add greeting only if new.
    if (greeting.isNotEmpty() && !greetingAdapter.containsGreeting(greeting)) {
        addGreeting(greeting)
    }
}
final EditText greetingEditText = findViewById(R.id.greeting_editText);

// Create adapter for recycler view.
greetingAdapter = new GreetingAdapter(this::removeGreeting);

// Setup recycler view.
RecyclerView recyclerView = findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(greetingAdapter);

// Add greeting on add button clicked.
Button addButton = findViewById(R.id.add_button);
addButton.setOnClickListener(v -> {
    String greeting = greetingEditText.getText().toString();
    greetingEditText.setText("");
    // Add greeting only if new.
    if (!greeting.isEmpty() && !greetingAdapter.containsGreeting(greeting)) {
        addGreeting(greeting);
    }
});

The GreetingAdapter implementation is available here: GitHub.

Modify the addGreeting and removeGreeting methods to update the list:

greetings.async().addPhrases(listOf(Phrase(greeting))).andThenConsume(Qi.onUiThread(Consumer {
                greetingAdapter.addGreeting(greeting)
}))

private fun addGreeting(greeting: String) {
    greetings?.async()?.addPhrases(singletonList(Phrase(greeting)))
            ?.andThenConsume(Qi.onUiThread(Consumer<Void> { greetingAdapter?.addGreeting(greeting) }))
}

private fun removeGreeting(greeting: String) {
    greetings?.async()?.removePhrases(singletonList(Phrase(greeting)))
            ?.andThenConsume(Qi.onUiThread(Consumer<Void> { greetingAdapter?.removeGreeting(greeting) }))
}
private void addGreeting(final String greeting) {
    if (greetings != null) {
        greetings.async().addPhrases(Collections.singletonList(new Phrase(greeting)))
                .andThenConsume(Qi.onUiThread((Consumer<Void>) ignore -> greetingAdapter.addGreeting(greeting)));
    }
}

private void removeGreeting(final String greeting) {
    if (greetings != null) {
        greetings.async().removePhrases(Collections.singletonList(new Phrase(greeting)))
                .andThenConsume(Qi.onUiThread((Consumer<Void>) ignore -> greetingAdapter.removeGreeting(greeting)));
    }
}

Let’s try it

github_icon The sources for this tutorial are available on GitHub.

Step Action

Install and run the application.

For further details, see: Running an application.

Choose “Mastering dynamic concept”.

The list of greetings contains “Hello” and “Hi”.

Say “Hello”.

Pepper says “Hello”.

Add “Good morning” and say “Hi”.

Pepper says “Good morning”.

../../../_images/dynamic_concept.png

You are now able create a dynamic QiChatbot!