Skip to content

Instantly share code, notes, and snippets.

@thiagobutignon
Last active August 21, 2024 22:11
Show Gist options
  • Save thiagobutignon/d36c06e1feb6a82479494f96cbbfeaf2 to your computer and use it in GitHub Desktop.
Save thiagobutignon/d36c06e1feb6a82479494f96cbbfeaf2 to your computer and use it in GitHub Desktop.
/**
* This file is part of the Softbank Robotics tutorial project, specifically focused on animating
* the Pepper robot using the QiSDK framework. It demonstrates how to build and run animations
* alongside playing sound effects and handling robot lifecycle callbacks.
*
* @file AnimateTutorialActivity.kt
* @author Softbank Robotics Europe
* @since 2018
*/
package com.softbankrobotics.qisdktutorials.ui.tutorials.motion.animate
import android.media.MediaPlayer
import android.os.Bundle
import android.util.Log
import com.aldebaran.qi.sdk.QiContext
import com.aldebaran.qi.sdk.QiSDK
import com.aldebaran.qi.sdk.RobotLifecycleCallbacks
import com.aldebaran.qi.sdk.builder.AnimateBuilder
import com.aldebaran.qi.sdk.builder.AnimationBuilder
import com.aldebaran.qi.sdk.builder.SayBuilder
import com.aldebaran.qi.sdk.`object`.actuation.Animate
import com.softbankrobotics.qisdktutorials.R
import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationBinder
import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationItemType
import com.softbankrobotics.qisdktutorials.ui.tutorials.TutorialActivity
import kotlinx.android.synthetic.main.conversation_layout.*
private const val TAG = "AnimateTutorialActivity" // Log tag for debugging purposes.
/**
* AnimateTutorialActivity is an activity that demonstrates how to create and execute an
* animation on the Pepper robot. It also shows how to play sound effects during the animation
* and log key events such as the start and end of the animation.
*/
class AnimateTutorialActivity : TutorialActivity(), RobotLifecycleCallbacks {
private var conversationBinder: ConversationBinder? = null // Binder for handling conversation view updates.
private var mediaPlayer: MediaPlayer? = null // MediaPlayer instance for playing sound effects.
private var animate: Animate? = null // Reference to the Animate action for later use.
/**
* Called when the activity is first created. Registers the RobotLifecycleCallbacks to this Activity.
* @param savedInstanceState Bundle containing the activity's previously saved state.
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
QiSDK.register(this, this) // Register the activity to handle robot lifecycle events.
}
/**
* Called when the activity becomes visible to the user. Initializes the MediaPlayer with a sound resource.
*/
override fun onStart() {
super.onStart()
mediaPlayer = MediaPlayer.create(this, R.raw.elephant_sound) // Load elephant sound effect.
}
/**
* Called when the activity is no longer visible to the user. Releases the MediaPlayer resources.
*/
override fun onStop() {
mediaPlayer?.release() // Release the MediaPlayer resources.
mediaPlayer = null
super.onStop()
}
/**
* Called when the activity is about to be destroyed. Unregisters the RobotLifecycleCallbacks for this Activity.
*/
override fun onDestroy() {
QiSDK.unregister(this, this) // Unregister the activity from handling robot lifecycle events.
super.onDestroy()
}
/**
* Specifies the layout resource to be used by this activity.
*/
override val layoutId = R.layout.conversation_layout
/**
* Called when the robot gains focus. This is where the main logic of building and running the animation is executed.
* @param qiContext The context in which the robot operates, used for building actions and animations.
*/
override fun onRobotFocusGained(qiContext: QiContext) {
// Bind the conversational events to the view.
val conversationStatus = qiContext.conversation.status(qiContext.robotContext)
conversationBinder = conversation_view.bindConversationTo(conversationStatus)
// Create a Say action to announce the start of the animation.
val say = SayBuilder.with(qiContext)
.withText("I can perform animations: here is an elephant.")
.build()
say.run() // Execute the Say action.
// Create an animation using the specified resource.
val animation = AnimationBuilder.with(qiContext)
.withResources(R.raw.elephant_a001) // Set the animation resource.
.build()
// Create an Animate action with the previously built animation.
val animate = AnimateBuilder.with(qiContext)
.withAnimation(animation) // Set the animation to be executed.
.build()
.also { this.animate = it } // Store the Animate action for later use.
// Add a listener to handle the event when the animation starts.
animate.addOnStartedListener {
val message = "Animation started." // Log message for when the animation starts.
Log.i(TAG, message)
displayLine(message, ConversationItemType.INFO_LOG) // Display the message in the conversation view.
mediaPlayer?.start() // Play the sound effect when the animation starts.
}
// Run the Animate action asynchronously.
val animateFuture = animate.async().run()
// Handle the result of the Animate action.
animateFuture.thenConsume {
if (it.isSuccess) {
val message = "Animation finished with success." // Log message for successful completion.
Log.i(TAG, message)
displayLine(message, ConversationItemType.INFO_LOG) // Display success message.
} else if (it.hasError()) {
val message = "Animation finished with error." // Log message for failed completion.
Log.e(TAG, message, it.error)
displayLine(message, ConversationItemType.ERROR_LOG) // Display error message.
}
}
}
/**
* Called when the robot loses focus. Cleans up resources related to the robot's actions.
*/
override fun onRobotFocusLost() {
conversationBinder?.unbind() // Unbind the conversation view from the robot's conversational status.
// Remove all listeners from the Animate action to prevent memory leaks.
animate?.removeAllOnStartedListeners()
}
/**
* Called when the robot focus is refused, typically due to another process having control. No action is needed here.
* @param reason The reason why the focus was refused.
*/
override fun onRobotFocusRefused(reason: String) {
// No specific action required when focus is refused.
}
/**
* Displays a message in the conversation view UI component.
* @param text The message to display.
* @param type The type of message (info, error, etc.) for appropriate styling.
*/
private fun displayLine(text: String, type: ConversationItemType) {
runOnUiThread { conversation_view.addLine(text, type) } // Ensures UI updates are done on the main thread.
}
}
@thiagobutignon
Copy link
Author

///**
// * This file is part of the Softbank Robotics tutorial project, specifically focused on animating
// * the Pepper robot using the QiSDK framework. It demonstrates how to build and run animations
// * alongside playing sound effects and handling robot lifecycle callbacks.
// *
// * @file AnimateTutorialActivity.kt
// * @author Softbank Robotics Europe
// * @SInCE 2018
// /
//
//package com.softbankrobotics.qisdktutorials.ui.tutorials.motion.animate
//
//import android.media.MediaPlayer
//import android.os.Bundle
//import android.util.Log
//
//import com.aldebaran.qi.sdk.QiContext
//import com.aldebaran.qi.sdk.QiSDK
//import com.aldebaran.qi.sdk.RobotLifecycleCallbacks
//import com.aldebaran.qi.sdk.builder.AnimateBuilder
//import com.aldebaran.qi.sdk.builder.AnimationBuilder
//import com.aldebaran.qi.sdk.builder.SayBuilder
//import com.aldebaran.qi.sdk.object.actuation.Animate
//import com.softbankrobotics.qisdktutorials.R
//import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationBinder
//import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationItemType
//import com.softbankrobotics.qisdktutorials.ui.tutorials.TutorialActivity
//import kotlinx.android.synthetic.main.conversation_layout.

//
//private const val TAG = "AnimateTutorialActivity" // Log tag for debugging purposes.
//
///**
// * AnimateTutorialActivity is an activity that demonstrates how to create and execute an
// * animation on the Pepper robot. It also shows how to play sound effects during the animation
// * and log key events such as the start and end of the animation.
// /
//class AnimateTutorialActivity : TutorialActivity(), RobotLifecycleCallbacks {
//
// private var conversationBinder: ConversationBinder? = null // Binder for handling conversation view updates.
// private var mediaPlayer: MediaPlayer? = null // MediaPlayer instance for playing sound effects.
// private var animate: Animate? = null // Reference to the Animate action for later use.
//
// /
*
// * Called when the activity is first created. Registers the RobotLifecycleCallbacks to this Activity.
// * @param savedInstanceState Bundle containing the activity's previously saved state.
// /
// override fun onCreate(savedInstanceState: Bundle?) {
// super.onCreate(savedInstanceState)
// QiSDK.register(this, this) // Register the activity to handle robot lifecycle events.
// }
//
// /
*
// * Called when the activity becomes visible to the user. Initializes the MediaPlayer with a sound resource.
// /
// override fun onStart() {
// super.onStart()
// mediaPlayer = MediaPlayer.create(this, R.raw.elephant_sound) // Load elephant sound effect.
// }
//
// /
*
// * Called when the activity is no longer visible to the user. Releases the MediaPlayer resources.
// /
// override fun onStop() {
// mediaPlayer?.release() // Release the MediaPlayer resources.
// mediaPlayer = null
// super.onStop()
// }
//
// /
*
// * Called when the activity is about to be destroyed. Unregisters the RobotLifecycleCallbacks for this Activity.
// /
// override fun onDestroy() {
// QiSDK.unregister(this, this) // Unregister the activity from handling robot lifecycle events.
// super.onDestroy()
// }
//
// /
*
// * Specifies the layout resource to be used by this activity.
// /
// override val layoutId = R.layout.conversation_layout
//
// /
*
// * Called when the robot gains focus. This is where the main logic of building and running the animation is executed.
// * @param qiContext The context in which the robot operates, used for building actions and animations.
// /
// override fun onRobotFocusGained(qiContext: QiContext) {
// // Bind the conversational events to the view.
// val conversationStatus = qiContext.conversation.status(qiContext.robotContext)
// conversationBinder = conversation_view.bindConversationTo(conversationStatus)
//
// // Create a Say action to announce the start of the animation.
// val say = SayBuilder.with(qiContext)
// .withText("I can perform animations: here is an elephant.")
// .build()
//
// say.run() // Execute the Say action.
//
// // Create an animation using the specified resource.
// val animation = AnimationBuilder.with(qiContext)
// .withResources(R.raw.elephant_a001) // Set the animation resource.
// .build()
//
// // Create an Animate action with the previously built animation.
// val animate = AnimateBuilder.with(qiContext)
// .withAnimation(animation) // Set the animation to be executed.
// .build()
// .also { this.animate = it } // Store the Animate action for later use.
//
// // Add a listener to handle the event when the animation starts.
// animate.addOnStartedListener {
// val message = "Animation started." // Log message for when the animation starts.
// Log.i(TAG, message)
// displayLine(message, ConversationItemType.INFO_LOG) // Display the message in the conversation view.
//
// mediaPlayer?.start() // Play the sound effect when the animation starts.
// }
//
// // Run the Animate action asynchronously.
// val animateFuture = animate.async().run()
//
// // Handle the result of the Animate action.
// animateFuture.thenConsume {
// if (it.isSuccess) {
// val message = "Animation finished with success." // Log message for successful completion.
// Log.i(TAG, message)
// displayLine(message, ConversationItemType.INFO_LOG) // Display success message.
// } else if (it.hasError()) {
// val message = "Animation finished with error." // Log message for failed completion.
// Log.e(TAG, message, it.error)
// displayLine(message, ConversationItemType.ERROR_LOG) // Display error message.
// }
// }
// }
//
//// override fun onRobotFocusGained(qiContext: QiContext) {
//// // Bind the conversational events to the view.
//// val conversationStatus = qiContext.conversation.status(qiContext.robotContext)
//// conversationBinder = conversation_view.bindConversationTo(conversationStatus)
////
//// // Create a Say action to announce the start of the animation.
//// val say = SayBuilder.with(qiContext)
//// .withText("I can perform animations: here is an elephant.")
//// .build()
////
//// say.run() // Execute the Say action.
////
//// // Start playing the sound effect.
//// mediaPlayer?.start()
////
//// // Loop the animation while the audio is playing.
//// loopAnimationWhileAudioPlaying(qiContext)
//// }
//
// /
*
// * Called when the robot loses focus. Cleans up resources related to the robot's actions.
// /
// override fun onRobotFocusLost() {
// conversationBinder?.unbind() // Unbind the conversation view from the robot's conversational status.
//
// // Remove all listeners from the Animate action to prevent memory leaks.
// animate?.removeAllOnStartedListeners()
// }
//
//// override fun onRobotFocusLost() {
//// conversationBinder?.unbind() // Unbind the conversation view from the robot's conversational status.
////
//// // Remove all listeners from the Animate action to prevent memory leaks.
//// animate?.removeAllOnStartedListeners()
////
//// // Stop the media player if it's still playing.
//// mediaPlayer?.stop()
//// }
//
// /
*
// * Called when the robot focus is refused, typically due to another process having control. No action is needed here.
// * @param reason The reason why the focus was refused.
// /
// override fun onRobotFocusRefused(reason: String) {
// // No specific action required when focus is refused.
// }
//
// /
*
// * Displays a message in the conversation view UI component.
// * @param text The message to display.
// * @param type The type of message (info, error, etc.) for appropriate styling.
// /
// private fun displayLine(text: String, type: ConversationItemType) {
// runOnUiThread { conversation_view.addLine(text, type) } // Ensures UI updates are done on the main thread.
// }
//
// /
*
// * Loops the animation until the audio stops playing.
// * @param qiContext The context in which the robot operates, used for building actions and animations.
// */
// private fun loopAnimationWhileAudioPlaying(qiContext: QiContext) {
// // Create an animation using the specified resource.
// val animation = AnimationBuilder.with(qiContext)
// .withResources(R.raw.elephant_a001) // Set the animation resource.
// .build()
//
// // Create an Animate action with the previously built animation.
// val animate = AnimateBuilder.with(qiContext)
// .withAnimation(animation) // Set the animation to be executed.
// .build()
// .also { this.animate = it } // Store the Animate action for later use.
//
// // Add a listener to handle the event when the animation starts.
// animate.addOnStartedListener {
// val message = "Animation started." // Log message for when the animation starts.
// Log.i(TAG, message)
// displayLine(message, ConversationItemType.INFO_LOG) // Display the message in the conversation view.
// }
//
// // Loop the animation until the media player is playing.
// while (mediaPlayer?.isPlaying == true) {
// val animateFuture = animate.async().run() // Run the Animate action asynchronously.
//
// // Handle the result of the Animate action.
// animateFuture.thenConsume {
// if (it.isSuccess) {
// val message = "Animation finished with success, repeating..." // Log message for successful completion.
// Log.i(TAG, message)
// displayLine(message, ConversationItemType.INFO_LOG) // Display success message.
// } else if (it.hasError()) {
// val message = "Animation finished with error, repeating..." // Log message for failed completion.
// Log.e(TAG, message, it.error)
// displayLine(message, ConversationItemType.ERROR_LOG) // Display error message.
// }
// }
// }
//
// val message = "Audio finished, stopping animation loop." // Log message when audio is done.
// Log.i(TAG, message)
// displayLine(message, ConversationItemType.INFO_LOG) // Display stop message.
// }
//}

package com.softbankrobotics.qisdktutorials.ui.tutorials.motion.animate

import android.media.MediaPlayer
import android.os.Bundle
import android.util.Log

import com.aldebaran.qi.sdk.QiContext
import com.aldebaran.qi.sdk.QiSDK
import com.aldebaran.qi.sdk.RobotLifecycleCallbacks
import com.aldebaran.qi.sdk.builder.AnimateBuilder
import com.aldebaran.qi.sdk.builder.AnimationBuilder
import com.aldebaran.qi.sdk.builder.SayBuilder
import com.aldebaran.qi.sdk.object.actuation.Animate
import com.softbankrobotics.qisdktutorials.R
import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationBinder
import com.softbankrobotics.qisdktutorials.ui.conversation.ConversationItemType
import com.softbankrobotics.qisdktutorials.ui.tutorials.TutorialActivity
import kotlinx.android.synthetic.main.conversation_layout.*

private const val TAG = "AnimateTutorialActivity" // Log tag for debugging purposes.

class AnimateTutorialActivity : TutorialActivity(), RobotLifecycleCallbacks {

private var conversationBinder: ConversationBinder? = null // Binder for handling conversation view updates.
private var mediaPlayer: MediaPlayer? = null // MediaPlayer instance for playing sound effects.
private var animate: Animate? = null // Reference to the Animate action for later use.
private var isAnimating = false // Flag to track if the animation is currently running.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    QiSDK.register(this, this) // Register the activity to handle robot lifecycle events.
}

override fun onStart() {
    super.onStart()
    mediaPlayer = MediaPlayer.create(this, R.raw.elephant_sound) // Load elephant sound effect.
}

override fun onStop() {
    mediaPlayer?.release() // Release the MediaPlayer resources.
    mediaPlayer = null
    super.onStop()
}

override fun onDestroy() {
    QiSDK.unregister(this, this) // Unregister the activity from handling robot lifecycle events.
    super.onDestroy()
}

override val layoutId = R.layout.conversation_layout

override fun onRobotFocusGained(qiContext: QiContext) {
    // Bind the conversational events to the view.
    val conversationStatus = qiContext.conversation.status(qiContext.robotContext)
    conversationBinder = conversation_view.bindConversationTo(conversationStatus)

    // Create a Say action to announce the start of the animation.
    val say = SayBuilder.with(qiContext)
        .withText("I can perform animations: here is an elephant.")
        .build()

    say.run() // Execute the Say action.

    // Start playing the sound effect.
    mediaPlayer?.start()

    // Loop the animation while the audio is playing.
    loopAnimationWhileAudioPlaying(qiContext)
}

private fun loopAnimationWhileAudioPlaying(qiContext: QiContext) {
    // Create an animation using the specified resource.
    val animation = AnimationBuilder.with(qiContext)
        .withResources(R.raw.elephant_a001) // Set the animation resource.
        .build()

    // Create an Animate action with the previously built animation.
    animate = AnimateBuilder.with(qiContext)
        .withAnimation(animation) // Set the animation to be executed.
        .build()

    // Loop the animation until the media player stops playing.
    mediaPlayer?.setOnCompletionListener {
        isAnimating = false
        val message = "Audio finished, stopping animation loop."
        Log.i(TAG, message)
        displayLine(message, ConversationItemType.INFO_LOG)
    }

    // Run the animation in a loop while the media player is playing.
    isAnimating = true
    while (mediaPlayer?.isPlaying == true && isAnimating) {
        animate?.run() // Run the Animate action synchronously.

        val message = "Animation loop finished, repeating..."
        Log.i(TAG, message)
        displayLine(message, ConversationItemType.INFO_LOG)
    }

    val message = "Animation stopped."
    Log.i(TAG, message)
    displayLine(message, ConversationItemType.INFO_LOG)
}

override fun onRobotFocusLost() {
    conversationBinder?.unbind() // Unbind the conversation view from the robot's conversational status.

    // Stop the animation loop.
    isAnimating = false

    // Remove all listeners from the Animate action to prevent memory leaks.
    animate?.removeAllOnStartedListeners()

    // Stop the media player if it's still playing.
    mediaPlayer?.stop()
}

override fun onRobotFocusRefused(reason: String) {
    // No specific action required when focus is refused.
}

private fun displayLine(text: String, type: ConversationItemType) {
    runOnUiThread { conversation_view.addLine(text, type) } // Ensures UI updates are done on the main thread.
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment