Added Pepper abstraction for synchronous events to prevent app from crashing
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
|
||||
public abstract class AbstractPepperActionEvent {
|
||||
|
||||
public final QiContext context;
|
||||
|
||||
public AbstractPepperActionEvent(QiContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public abstract EPepperAction getAction();
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
/**
|
||||
* Enum for Pepper actions
|
||||
*/
|
||||
public enum EPepperAction {
|
||||
ACTION_SPEAK,
|
||||
ACTION_ANIMATE,
|
||||
NO_ACTION;
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
import com.aldebaran.qi.sdk.object.locale.Language;
|
||||
import com.aldebaran.qi.sdk.object.locale.Locale;
|
||||
import com.aldebaran.qi.sdk.object.locale.Region;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Class representing the Pepper robot.
|
||||
* All functionality related to Pepper is implemented in this class.
|
||||
*/
|
||||
public class Pepper {
|
||||
|
||||
private static final ConcurrentLinkedQueue<AbstractPepperActionEvent> pepperActionEventQueue =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
private static final AtomicBoolean isAnimating = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean isSpeaking = new AtomicBoolean(false);
|
||||
|
||||
private static final Locale DEFAULT_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS);
|
||||
|
||||
/**
|
||||
* Speak a phrase with Pepper.
|
||||
*
|
||||
* @param phrase The phrase to speak
|
||||
* @param context The QiContext to use
|
||||
*/
|
||||
public static void speak(String phrase, QiContext context)
|
||||
{
|
||||
addToEventQueue(new PepperSpeechEvent(context, phrase, DEFAULT_LOCALE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate Pepper with the given animation.
|
||||
*
|
||||
* @param animationName The name of the animation to play
|
||||
* @param context The QiContext to use
|
||||
*/
|
||||
public static void animate(String animationName, QiContext context)
|
||||
{
|
||||
addToEventQueue(new PepperAnimationEvent(context, animationName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event to the event queue.
|
||||
* The event will be executed once Pepper is ready to process it.
|
||||
*
|
||||
* @param event The event to add to the queue
|
||||
*/
|
||||
public static void addToEventQueue(AbstractPepperActionEvent event)
|
||||
{
|
||||
pepperActionEventQueue.add(event);
|
||||
processEventQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the event queue.
|
||||
* This method will process the next event in the queue if Pepper is not currently
|
||||
* speaking or animating.
|
||||
* This prevents multiple actions from being executed at the same time, which can
|
||||
* cause the application to crash.
|
||||
*/
|
||||
private static synchronized void processEventQueue()
|
||||
{
|
||||
if (!pepperActionEventQueue.isEmpty())
|
||||
{
|
||||
AbstractPepperActionEvent event = pepperActionEventQueue.poll();
|
||||
|
||||
if (event == null)
|
||||
return;
|
||||
|
||||
switch (event.getAction())
|
||||
{
|
||||
/* Event for speaking a phrase **/
|
||||
case ACTION_SPEAK:
|
||||
if ( !(event instanceof PepperSpeechEvent) || isSpeaking.get())
|
||||
break;
|
||||
PepperSpeechEvent speechEvent = (PepperSpeechEvent) event;
|
||||
isSpeaking.set(true);
|
||||
speechEvent
|
||||
.sayPhrase()
|
||||
.async()
|
||||
.run()
|
||||
.andThenConsume(future -> {
|
||||
isSpeaking.set(false);
|
||||
processEventQueue();
|
||||
});
|
||||
break;
|
||||
|
||||
/* Event for animating the robot **/
|
||||
case ACTION_ANIMATE:
|
||||
if ( !(event instanceof PepperAnimationEvent) || isAnimating.get())
|
||||
break;
|
||||
PepperAnimationEvent animationEvent = (PepperAnimationEvent) event;
|
||||
animationEvent
|
||||
.getAnimation()
|
||||
.async()
|
||||
.run()
|
||||
.andThenConsume(future -> {
|
||||
isAnimating.set(false);
|
||||
processEventQueue();
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
import com.aldebaran.qi.sdk.builder.AnimateBuilder;
|
||||
import com.aldebaran.qi.sdk.builder.AnimationBuilder;
|
||||
import com.aldebaran.qi.sdk.object.actuation.Animate;
|
||||
import com.aldebaran.qi.sdk.object.actuation.Animation;
|
||||
|
||||
public class PepperAnimationEvent extends AbstractPepperActionEvent {
|
||||
|
||||
public final String animationName;
|
||||
|
||||
public PepperAnimationEvent(QiContext context, String animationName) {
|
||||
super(context);
|
||||
this.animationName = animationName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EPepperAction getAction() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an animation object, which can be used to play the animation
|
||||
* in the Pepper class.
|
||||
*/
|
||||
public Animate getAnimation()
|
||||
{
|
||||
Animation animation = AnimationBuilder.with(this.context)
|
||||
.withResources(this.context.getResources().getIdentifier(animationName, "raw", this.context.getPackageName()))
|
||||
.build();
|
||||
|
||||
return AnimateBuilder.with(this.context)
|
||||
.withAnimation(animation)
|
||||
.build();
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
import com.aldebaran.qi.sdk.builder.SayBuilder;
|
||||
import com.aldebaran.qi.sdk.object.conversation.Say;
|
||||
import com.aldebaran.qi.sdk.object.locale.Locale;
|
||||
|
||||
public class PepperSpeechEvent extends AbstractPepperActionEvent {
|
||||
|
||||
public final String phrase;
|
||||
public final Locale locale;
|
||||
|
||||
public PepperSpeechEvent(QiContext context, String phrase, Locale locale)
|
||||
{
|
||||
super(context);
|
||||
this.locale = locale;
|
||||
this.phrase = phrase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EPepperAction getAction() {
|
||||
return EPepperAction.ACTION_SPEAK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Say object, which can then be executed.
|
||||
*/
|
||||
public Say sayPhrase()
|
||||
{
|
||||
return SayBuilder.with(this.context)
|
||||
.withText(this.phrase)
|
||||
.withLocale(this.locale)
|
||||
.build();
|
||||
}
|
||||
}
|
@@ -11,6 +11,7 @@ import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayS
|
||||
import com.example.fitbot.R;
|
||||
import com.example.fitbot.exercise.Exercise;
|
||||
import com.example.fitbot.exercise.ExerciseManager;
|
||||
import com.example.fitbot.pepper.Pepper;
|
||||
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
||||
import com.example.fitbot.util.NavigationManager;
|
||||
import com.example.fitbot.util.FitnessCycle;
|
||||
@@ -31,7 +32,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
private static final String[] EXERCISE_NOT_FOUND_MESSAGES = new String[] {
|
||||
"Ik heb momenteel helaas wat moeite met het ophalen van oefeningen, sorry.",
|
||||
"Het lijkt erop dat de oefeningen op een misterieus avontuur zijn. Even wachten tot ze terug zijn.",
|
||||
"Ssst, de oefeningen slapen nog, probeer het later nog eens."
|
||||
", de oefeningen slapen nog, probeer het later nog eens."
|
||||
};
|
||||
|
||||
private static final String EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE =
|
||||
@@ -84,9 +85,9 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
motionProcessor.setInputHandler(personalMotionPreviewElement);
|
||||
}, (n) -> {
|
||||
int randomMessageIndex = (int)Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||
FitnessCycle.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
||||
FitnessCycle.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, qiContext);
|
||||
|
||||
Pepper.speak(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
||||
Pepper.speak(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, qiContext);
|
||||
NavigationManager.navigateToActivity(this, MainActivity.class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,15 +122,15 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRobotFocusRefused(String reason) {
|
||||
QiSDK.unregister(this, this);
|
||||
}
|
||||
public void onRobotFocusRefused(String reason) {}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
QiSDK.unregister(this, this);
|
||||
if ( this.motionProcessor != null ) {
|
||||
this.motionProcessor.stopListening();
|
||||
this.motionProcessor = null;
|
||||
}
|
||||
QiSDK.unregister(this, this);
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
@@ -130,13 +130,13 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
this.motionProcessor.useExercise(newExercise);
|
||||
}, (failed) -> {
|
||||
// Move to main screen
|
||||
NavigationManager.navigateToActivity(getContext(), MainActivity.class);
|
||||
NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Finish the exercise.
|
||||
NavigationManager.navigateToActivity(getContext(), EndScreenActivity.class);
|
||||
NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -125,12 +125,18 @@ public class FitnessCycle extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private static class PepperSpeechEntry {
|
||||
private static class PepperAnimateEvent extends PepperActionEvent {
|
||||
|
||||
}
|
||||
|
||||
private static class PepperSpeechEntry extends PepperActionEvent {
|
||||
public String phrase;
|
||||
public QiContext context;
|
||||
public Locale locale;
|
||||
|
||||
public PepperSpeechEntry(String phrase, QiContext context, Locale locale) {
|
||||
this.action = PepperAction.SPEAK;
|
||||
this.data = this;
|
||||
this.phrase = phrase;
|
||||
this.context = context;
|
||||
this.locale = locale;
|
||||
@@ -144,4 +150,15 @@ public class FitnessCycle extends AppCompatActivity {
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private enum PepperAction {
|
||||
SPEAK,
|
||||
ANIMATE,
|
||||
MOVE
|
||||
}
|
||||
|
||||
private static class PepperActionEvent {
|
||||
public PepperAction action;
|
||||
public Object data;
|
||||
}
|
||||
}
|
@@ -20,21 +20,35 @@ public class NavigationManager {
|
||||
*/
|
||||
public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class<? extends Activity> targetActivity) {
|
||||
Button button = currentActivity.findViewById(buttonId);
|
||||
button.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(currentActivity, targetActivity);
|
||||
currentActivity.startActivity(intent);
|
||||
currentActivity.finish();
|
||||
});
|
||||
if ( button == null )
|
||||
{
|
||||
throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName());
|
||||
}
|
||||
button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the target activity.
|
||||
*
|
||||
* @param currentContext The current context
|
||||
* @param currentActivity The current activity
|
||||
* @param targetActivity The target activity
|
||||
*/
|
||||
public static void navigateToActivity(Context currentContext, Class<? extends Activity> targetActivity) {
|
||||
Intent intent = new Intent(currentContext, targetActivity);
|
||||
currentContext.startActivity(intent);
|
||||
public static void navigateToActivity(Activity currentActivity, Class<? extends Activity> targetActivity) {
|
||||
Intent intent = new Intent(currentActivity, targetActivity);
|
||||
// Close previous activity
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
currentActivity.startActivity(intent);
|
||||
currentActivity.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the target activity.
|
||||
*
|
||||
* @param context The context
|
||||
* @param targetActivity The target activity
|
||||
*/
|
||||
public static void navigateToActivity(Context context, Class<? extends Activity> targetActivity) {
|
||||
Intent intent = new Intent(context, targetActivity);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user