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.R;
|
||||||
import com.example.fitbot.exercise.Exercise;
|
import com.example.fitbot.exercise.Exercise;
|
||||||
import com.example.fitbot.exercise.ExerciseManager;
|
import com.example.fitbot.exercise.ExerciseManager;
|
||||||
|
import com.example.fitbot.pepper.Pepper;
|
||||||
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
||||||
import com.example.fitbot.util.NavigationManager;
|
import com.example.fitbot.util.NavigationManager;
|
||||||
import com.example.fitbot.util.FitnessCycle;
|
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[] {
|
private static final String[] EXERCISE_NOT_FOUND_MESSAGES = new String[] {
|
||||||
"Ik heb momenteel helaas wat moeite met het ophalen van oefeningen, sorry.",
|
"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.",
|
"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 =
|
private static final String EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE =
|
||||||
@@ -84,9 +85,9 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
|||||||
motionProcessor.setInputHandler(personalMotionPreviewElement);
|
motionProcessor.setInputHandler(personalMotionPreviewElement);
|
||||||
}, (n) -> {
|
}, (n) -> {
|
||||||
int randomMessageIndex = (int)Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
int randomMessageIndex = (int)Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||||
FitnessCycle.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
Pepper.speak(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
||||||
FitnessCycle.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, 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
|
@Override
|
||||||
public void onRobotFocusRefused(String reason) {
|
public void onRobotFocusRefused(String reason) {}
|
||||||
QiSDK.unregister(this, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
if ( this.motionProcessor != null ) {
|
||||||
|
this.motionProcessor.stopListening();
|
||||||
|
this.motionProcessor = null;
|
||||||
|
}
|
||||||
QiSDK.unregister(this, this);
|
QiSDK.unregister(this, this);
|
||||||
this.motionProcessor.stopListening();
|
super.onDestroy();
|
||||||
this.motionProcessor = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -130,13 +130,13 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
|||||||
this.motionProcessor.useExercise(newExercise);
|
this.motionProcessor.useExercise(newExercise);
|
||||||
}, (failed) -> {
|
}, (failed) -> {
|
||||||
// Move to main screen
|
// Move to main screen
|
||||||
NavigationManager.navigateToActivity(getContext(), MainActivity.class);
|
NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Finish the exercise.
|
// Finish the exercise.
|
||||||
NavigationManager.navigateToActivity(getContext(), EndScreenActivity.class);
|
NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
|
||||||
return;
|
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 String phrase;
|
||||||
public QiContext context;
|
public QiContext context;
|
||||||
public Locale locale;
|
public Locale locale;
|
||||||
|
|
||||||
public PepperSpeechEntry(String phrase, QiContext context, Locale locale) {
|
public PepperSpeechEntry(String phrase, QiContext context, Locale locale) {
|
||||||
|
this.action = PepperAction.SPEAK;
|
||||||
|
this.data = this;
|
||||||
this.phrase = phrase;
|
this.phrase = phrase;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
@@ -144,4 +150,15 @@ public class FitnessCycle extends AppCompatActivity {
|
|||||||
.build();
|
.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) {
|
public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class<? extends Activity> targetActivity) {
|
||||||
Button button = currentActivity.findViewById(buttonId);
|
Button button = currentActivity.findViewById(buttonId);
|
||||||
button.setOnClickListener(v -> {
|
if ( button == null )
|
||||||
Intent intent = new Intent(currentActivity, targetActivity);
|
{
|
||||||
currentActivity.startActivity(intent);
|
throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName());
|
||||||
currentActivity.finish();
|
}
|
||||||
});
|
button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the target activity.
|
* Navigates to the target activity.
|
||||||
*
|
*
|
||||||
* @param currentContext The current context
|
* @param currentActivity The current activity
|
||||||
* @param targetActivity The target activity
|
* @param targetActivity The target activity
|
||||||
*/
|
*/
|
||||||
public static void navigateToActivity(Context currentContext, Class<? extends Activity> targetActivity) {
|
public static void navigateToActivity(Activity currentActivity, Class<? extends Activity> targetActivity) {
|
||||||
Intent intent = new Intent(currentContext, targetActivity);
|
Intent intent = new Intent(currentActivity, targetActivity);
|
||||||
currentContext.startActivity(intent);
|
// 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