QiChatbot – Mastering Bookmark

Goal

In this tutorial, we will go to a specific part of a QiChatbot and react when a specific part is reached, using Bookmarks.

We will see 2 uses for a bookmark:
  • how to go to a specific bookmark,
  • how to be notified when a bookmark is reached.

Prerequisites

Before stepping in this tutorial, you should:

Let’s start a new project

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

For further details, see: Creating a robot application.

Creating a topic file

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

Add the following content to this file:

topic: ~mimic_animal()

proposal: %mimic_proposal Say dog or elephant
    u1: (dog) OK, I've reached the dog bookmark %dog_mimic
    u1: (elephant) OK, I've reached the elephant bookmark %elephant_mimic

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

How to go to a bookmark

In this section, we will see how to go to a specific Bookmark in a QiChatbot.

Store a QiChatbot, a Chat and a Bookmark in your MainActivity:

// Store the QiChatbot.
private var qiChatbot: QiChatbot? = null
// Store the Chat action.
private var chat: Chat? = null
// Store the proposal bookmark.
private var proposalBookmark: Bookmark? = null
// Store the QiChatbot.
private QiChatbot qiChatbot;
// Store the Chat action.
private Chat chat;
// Store the proposal bookmark.
private Bookmark proposalBookmark;

To go to a Bookmark, use the goToBookmark method. Add the following method to your MainActivity:

private fun sayProposal() {
    qiChatbot?.goToBookmark(proposalBookmark, AutonomousReactionImportance.HIGH, AutonomousReactionValidity.IMMEDIATE)
}
private void sayProposal() {
    qiChatbot.goToBookmark(proposalBookmark, AutonomousReactionImportance.HIGH, AutonomousReactionValidity.IMMEDIATE);
}

Note

We are using AutonomousReactionImportance.HIGH and AutonomousReactionValidity.IMMEDIATE so that the Chat goes to the specified bookmark right away.

Create a Topic from the previously created topic file. With this Topic, we then create a QiChatbot and a Chat action.

Put the following code in the onRobotFocusGained method:

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

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

// Create a new Chat action.
chat = ChatBuilder.with(qiContext)
        .withChatbot(qiChatbot)
        .build()
// Create a topic.
Topic topic = TopicBuilder.with(qiContext)
        .withResource(R.raw.mimic_animal)
        .build();

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

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

Retrieve all the bookmarks of the Topic and extract the mimic_proposal one.

// Get the bookmarks from the topic.
val bookmarks: Map<String, Bookmark> = topic.bookmarks
// Get the proposal bookmark.
proposalBookmark = bookmarks["mimic_proposal"]
// Get the bookmarks from the topic.
Map<String, Bookmark> bookmarks = topic.getBookmarks();
// Get the proposal bookmark.
proposalBookmark = bookmarks.get("mimic_proposal");

And execute the sayProposal method when the Chat action starts:

 // Go to the proposal bookmark when the Chat action starts.
chat?.addOnStartedListener(this::sayProposal)
// Go to the proposal bookmark when the Chat action starts.
chat.addOnStartedListener(this::sayProposal);

Do not forget to remove this listener on Chat in the onRobotFocusLost method:

// Remove on started listeners from the Chat action.
chat?.removeAllOnStartedListeners()
// Remove on started listeners from the Chat action.
if (chat != null) {
    chat.removeAllOnStartedListeners();
}

With this code, the robot is configured to say the proposal when the Chat starts.

If you run the application, Pepper will say the proposal.

Reacting to a bookmark

In this section, we will see how to react when a bookmark is reached. For that, we will use the BookmarkStatus class.

Store these fields in your MainActivity:

// Store the dog BookmarkStatus.
private var dogBookmarkStatus: BookmarkStatus? = null
// Store the elephant BookmarkStatus.
private var elephantBookmarkStatus: BookmarkStatus? = null
// Store the dog BookmarkStatus.
private BookmarkStatus dogBookmarkStatus;
// Store the elephant BookmarkStatus.
private BookmarkStatus elephantBookmarkStatus;

In the onRobotFocusGained method, get the bookmarks corresponding to the different user answers and create a BookmarkStatus for each bookmark:

// Get the mimic bookmarks.
val dogBookmark: Bookmark= bookmarks["dog_mimic"]
val elephantBookmark: Bookmark = bookmarks["elephant_mimic"]

// Create a BookmarkStatus for each bookmark.
dogBookmarkStatus = qiChatbot.bookmarkStatus(dogBookmark)
elephantBookmarkStatus = qiChatbot.bookmarkStatus(elephantBookmark)
// Get the mimic bookmarks.
Bookmark dogBookmark = bookmarks.get("dog_mimic");
Bookmark elephantBookmark = bookmarks.get("elephant_mimic");

// Create a BookmarkStatus for each bookmark.
dogBookmarkStatus = qiChatbot.bookmarkStatus(dogBookmark);
elephantBookmarkStatus = qiChatbot.bookmarkStatus(elephantBookmark);

Set a BookmarkStatus.OnReachedListener on each BookmarkStatus so that we can react when the corresponding bookmark is reached:

 dogBookmarkStatus?.addOnReachedListener {
    // React when the dog bookmark is reached.
}

elephantBookmarkStatus?.addOnReachedListener {
    // React when the elephant bookmark is reached.
}
dogBookmarkStatus.addOnReachedListener(() -> {
    // React when the dog bookmark is reached.
});

elephantBookmarkStatus.addOnReachedListener(() -> {
    // React when the elephant bookmark is reached.
});

Remove these listeners in the onRobotFocusLost method:

 // Remove the listeners on each BookmarkStatus.
dogBookmarkStatus?.removeAllOnReachedListeners()
elephantBookmarkStatus?.removeAllOnReachedListeners()
// Remove the listeners on each BookmarkStatus.
if (dogBookmarkStatus != null) {
    dogBookmarkStatus.removeAllOnReachedListeners();
}
if (elephantBookmarkStatus != null) {
    elephantBookmarkStatus.removeAllOnReachedListeners();
}

We now want Pepper to perform an animal animation depending on the user answer.

Import both dog_a001 and elephant_a001 animations using the Animation Browser. They are located in Action > Animals.

Create the following methods to perform animations:

private fun mimicDog qiContext: QiContext) {
    Log.i(TAG, "Dog mimic.")
    mimic(R.raw.dog_a001, qiContext)
}

private fun mimicElephant(qiContext: QiContext) {
    Log.i(TAG, "Elephant mimic.")
    mimic(R.raw.elephant_a001, qiContext)
}

private fun mimic(@RawRes mimicResource: Integer, qiContext: QiContext) {
    // Create an animation from the mimic resource.
    val animation: Animation = AnimationBuilder.with(qiContext)
            .withResources(mimicResource)
            .build()

    // Create an animate action.
    val animate: Animate = AnimateBuilder.with(qiContext)
            .withAnimation(animation)
            .build()

    // Run the animate action asynchronously.
    animate.async().run().andThenConsume { sayProposal() }
}
private void mimicDog(QiContext qiContext) {
    Log.i(TAG, "Dog mimic.");
    mimic(R.raw.dog_a001, qiContext);
}

private void mimicElephant(QiContext qiContext) {
    Log.i(TAG, "Elephant mimic.");
    mimic(R.raw.elephant_a001, qiContext);
}

private void mimic(@RawRes Integer mimicResource, QiContext qiContext) {
    // Create an animation from the mimic resource.
    Animation animation = AnimationBuilder.with(qiContext)
            .withResources(mimicResource)
            .build();

    // Create an animate action.
    Animate animate = AnimateBuilder.with(qiContext)
            .withAnimation(animation)
            .build();

    // Run the animate action asynchronously.
    animate.async().run().andThenConsume(ignore -> sayProposal());
}

Update the code inside the BookmarkStatus listeners to execute the animations:

// Mimic a dog when the dog mimic bookmark is reached.
dogBookmarkStatus?.addOnReachedListener {
    mimicDog(qiContext)
}

// Mimic an elephant when the elephant mimic bookmark is reached.
elephantBookmarkStatus?.addOnReachedListener {
    mimicElephant(qiContext)
}
// Mimic a dog when the dog mimic bookmark is reached.
dogBookmarkStatus?.addOnReachedListener(() -> {
    mimicDog(qiContext);
});

// Mimic an elephant when the elephant mimic bookmark is reached.
elephantBookmarkStatus.addOnReachedListener(() -> {
    mimicElephant(qiContext);
});

Last, we can run the Chat action:

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

Bonus: Adding some sound

Take advantage of the Animate.OnStartedListener to start some sound at the beginning of the animation, using the MediaPlayer class.

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 Bookmarks”.

Pepper says the proposal.

Answer “dog” or “elephant”.

Pepper performs the corresponding animation.

The log trace “<animal> mimic.” is also displayed in the console.

../../../_images/bookmarks.png

You are now able to use bookmarks in a QiChatbot!