diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/AbstractPepperActionEvent.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/AbstractPepperActionEvent.java new file mode 100644 index 0000000..d0c97f2 --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/AbstractPepperActionEvent.java @@ -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(); +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/EPepperAction.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/EPepperAction.java new file mode 100644 index 0000000..199f03e --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/EPepperAction.java @@ -0,0 +1,10 @@ +package com.example.fitbot.pepper; + +/** + * Enum for Pepper actions + */ +public enum EPepperAction { + ACTION_SPEAK, + ACTION_ANIMATE, + NO_ACTION; +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/Pepper.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/Pepper.java new file mode 100644 index 0000000..0d358dc --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/Pepper.java @@ -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 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; + } + } + } + +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java new file mode 100644 index 0000000..b91a8f8 --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java @@ -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(); + } +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java new file mode 100644 index 0000000..5d37db9 --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java @@ -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(); + } +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java index d93e8a6..a871e28 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java @@ -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(); + if ( this.motionProcessor != null ) { + this.motionProcessor.stopListening(); + this.motionProcessor = null; + } QiSDK.unregister(this, this); - this.motionProcessor.stopListening(); - this.motionProcessor = null; + super.onDestroy(); } } \ No newline at end of file diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java index d8ac0ec..9f3c123 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java @@ -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; } } diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java index afc3988..9f291dc 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java @@ -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; + } } \ No newline at end of file diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java index 0b1ec75..2a4d966 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java @@ -20,21 +20,35 @@ public class NavigationManager { */ public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class 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 targetActivity) { - Intent intent = new Intent(currentContext, targetActivity); - currentContext.startActivity(intent); + public static void navigateToActivity(Activity currentActivity, Class 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 targetActivity) { + Intent intent = new Intent(context, targetActivity); + context.startActivity(intent); } } \ No newline at end of file