Removed FitnessCycle, reformatted code, made Pepper actions globally accessible

This commit is contained in:
Luca Warmenhoven
2024-05-30 17:16:09 +02:00
parent b4ce40ca7d
commit 3f37f44f22
22 changed files with 234 additions and 428 deletions

View File

@@ -19,11 +19,10 @@ public enum EMuscleGroup {
return this.muscleGroupIdentifier; return this.muscleGroupIdentifier;
} }
public static EMuscleGroup parse(String name) public static EMuscleGroup parse(String name) {
{ for (EMuscleGroup muscleGroup : EMuscleGroup.values())
for ( EMuscleGroup muscleGroup : EMuscleGroup.values()) for (String muscleGroupName : muscleGroup.muscleGroupNames)
for ( String muscleGroupName : muscleGroup.muscleGroupNames) if (muscleGroupName.equalsIgnoreCase(name))
if ( muscleGroupName.equalsIgnoreCase(name))
return muscleGroup; return muscleGroup;
return null; return null;
} }

View File

@@ -1,12 +1,6 @@
package com.example.fitbot.exercise; package com.example.fitbot.exercise;
import android.util.Log;
import com.example.fitbot.util.path.GesturePath; import com.example.fitbot.util.path.GesturePath;
import com.example.fitbot.util.server.IWebServerHandler;
import com.example.fitbot.util.server.WebServer;
import java.util.Objects;
public class Exercise { public class Exercise {
@@ -22,13 +16,13 @@ public class Exercise {
/** /**
* Constructor for the AbstractExercise class. * Constructor for the AbstractExercise class.
* *
* @param muscleGroup The muscle group of the exercise. * @param muscleGroup The muscle group of the exercise.
* @param leftPath The path of the left hand. * @param leftPath The path of the left hand.
* @param rightPath The path of the right hand. * @param rightPath The path of the right hand.
* @param title The title of the exercise. * @param title The title of the exercise.
* @param description The description of the exercise. * @param description The description of the exercise.
* @param imageUrl The URL of the image. * @param imageUrl The URL of the image.
* @param videoUrl The URL of the video. * @param videoUrl The URL of the video.
*/ */
public Exercise(EMuscleGroup muscleGroup, String title, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) { public Exercise(EMuscleGroup muscleGroup, String title, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) {
this.muscleGroup = muscleGroup; this.muscleGroup = muscleGroup;

View File

@@ -18,15 +18,15 @@ public class ExerciseManager {
// The value of these property variables must be equivalent of // The value of these property variables must be equivalent of
// the JSON data that the database sends back. // the JSON data that the database sends back.
// If this is not the case then the exercise retrieval will fail. // If this is not the case then the exercise retrieval will fail.
private static final String PROPERTY_EXERCISE_DURATION = "duration"; private static final String PROPERTY_EXERCISE_DURATION = "duration";
private static final String PROPERTY_EXERCISE_ID = "exerciseId"; private static final String PROPERTY_EXERCISE_ID = "exerciseId";
private static final String PROPERTY_MUSCLE_GROUP = "muscleGroup"; private static final String PROPERTY_MUSCLE_GROUP = "muscleGroup";
private static final String PROPERTY_SHORT_DESC = "shortDescription"; private static final String PROPERTY_SHORT_DESC = "shortDescription";
private static final String PROPERTY_IMAGE_URL = "imageUrl"; private static final String PROPERTY_IMAGE_URL = "imageUrl";
private static final String PROPERTY_VIDEO_URL = "videoUrl"; private static final String PROPERTY_VIDEO_URL = "videoUrl";
private static final String PROPERTY_DESC = "description"; private static final String PROPERTY_DESC = "description";
private static final String PROPERTY_PATH = "path"; private static final String PROPERTY_PATH = "path";
private static final String PROPERTY_NAME = "name"; private static final String PROPERTY_NAME = "name";
public static final int SENSOR_COUNT = 2; public static final int SENSOR_COUNT = 2;
@@ -37,18 +37,17 @@ public class ExerciseManager {
PROPERTY_SHORT_DESC PROPERTY_SHORT_DESC
}; };
public static final int DEFAULT_EXERCISE_REPETITIONS = 10; public static final int DEFAULT_EXERCISE_REPETITIONS = 10;
public static final float DEFAULT_SEGMENT_SPEED = 1.0f; public static final float DEFAULT_SEGMENT_SPEED = 1.0f;
public static final float EXERCISE_ERROR_MARGIN = 1.0f; public static final float EXERCISE_ERROR_MARGIN = 1.0f;
/** /**
* Function for sending an HTTP request to the server. * Function for sending an HTTP request to the server.
* *
* @param url The URL to send the request to. * @param url The URL to send the request to.
* @param method The method to use for the request, e.g. GET or POST. * @param method The method to use for the request, e.g. GET or POST.
* @param contentType The content type of the request. * @param contentType The content type of the request.
* @param body The body of the request. * @param body The body of the request.
*
* @return The response from the server. * @return The response from the server.
*/ */
public static String sendHTTP(String url, String method, String contentType, String body) { public static String sendHTTP(String url, String method, String contentType, String body) {
@@ -61,7 +60,7 @@ public class ExerciseManager {
connection.connect(); connection.connect();
// Send a body if it is present // Send a body if it is present
if (body != null) if (body != null)
connection.getOutputStream().write(body.getBytes()); connection.getOutputStream().write(body.getBytes());
InputStream stream = connection.getInputStream(); InputStream stream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@@ -102,7 +101,7 @@ public class ExerciseManager {
// class to support more paths. // class to support more paths.
String[] leftRightData = content.get(PROPERTY_PATH).getAsString().split(";"); String[] leftRightData = content.get(PROPERTY_PATH).getAsString().split(";");
if ( leftRightData.length != SENSOR_COUNT) if (leftRightData.length != SENSOR_COUNT)
return null; return null;
return new Exercise( return new Exercise(

View File

@@ -1,53 +0,0 @@
package com.example.fitbot.exercise;
/**
* The ExerciseUser class represents a user of the exercise application.
* This contains all necessary information of the current user.
*/
public class ExerciseUser {
public float upperArmLength;
public float lowerArmLength;
public float upperLegLength;
public float lowerLegLength;
public float height;
/**
* Constructor for the ExerciseUser class.
* @param upperArmLength The length of the upper arm.
* @param lowerArmLength The length of the lower arm.
* @param height The height of the user.
* @param upperLegLength The length of the upper leg.
* @param lowerLegLength The length of the lower leg.
*/
public ExerciseUser(float upperArmLength, float lowerArmLength, float height, float upperLegLength, float lowerLegLength) {
this.upperArmLength = upperArmLength;
this.lowerArmLength = lowerArmLength;
this.upperLegLength = upperLegLength;
this.lowerLegLength = lowerLegLength;
this.height = height;
}
/**
* Constructor for the ExerciseUser class.
* @param height The height of the user.
*/
public ExerciseUser(float height) {
this.height = height;
this.upperArmLength = height * 0.2f;
this.lowerArmLength = height * 0.2f;
this.upperLegLength = height * 0.3f;
this.lowerLegLength = height * 0.3f;
}
/**
* Default constructor for the ExerciseUser class.
* This sets the default height to 180.0f. (1.80m)
*/
public ExerciseUser() {
this(180.0f);
}
}

View File

@@ -1,15 +1,6 @@
package com.example.fitbot.pepper; package com.example.fitbot.pepper;
import com.aldebaran.qi.sdk.QiContext;
public abstract class AbstractPepperActionEvent { public abstract class AbstractPepperActionEvent {
public final QiContext context;
public AbstractPepperActionEvent(QiContext context)
{
this.context = context;
}
public abstract EPepperAction getAction(); public abstract EPepperAction getAction();
} }

View File

@@ -6,5 +6,5 @@ package com.example.fitbot.pepper;
public enum EPepperAction { public enum EPepperAction {
ACTION_SPEAK, ACTION_SPEAK,
ACTION_ANIMATE, ACTION_ANIMATE,
NO_ACTION; NO_ACTION
} }

View File

@@ -1,6 +1,7 @@
package com.example.fitbot.pepper; package com.example.fitbot.pepper;
import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.QiContext;
import com.aldebaran.qi.sdk.RobotLifecycleCallbacks;
import com.aldebaran.qi.sdk.object.locale.Language; import com.aldebaran.qi.sdk.object.locale.Language;
import com.aldebaran.qi.sdk.object.locale.Locale; import com.aldebaran.qi.sdk.object.locale.Locale;
import com.aldebaran.qi.sdk.object.locale.Region; import com.aldebaran.qi.sdk.object.locale.Region;
@@ -11,37 +12,50 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Class representing the Pepper robot. * Class representing the Pepper robot.
* All functionality related to Pepper is implemented in this class. * All functionality related to Pepper is implemented in this class.
* <p>
* Before any of the functions in this class can be used, the Pepper class needs a QiContext to be
* set. This is retrieved in the `onRobotFocusGained` method of the RobotLifecycleCallbacks
* interface, and can then provided using the `provideContext` method.
* </p>
*/ */
public class Pepper { public class Pepper {
// Queue containing all Pepper actions that need to be executed
// This is to prevent the app from crashing when attempting to perform multiple actions at once
private static final ConcurrentLinkedQueue<AbstractPepperActionEvent> pepperActionEventQueue = private static final ConcurrentLinkedQueue<AbstractPepperActionEvent> pepperActionEventQueue =
new ConcurrentLinkedQueue<>(); new ConcurrentLinkedQueue<>();
private static final AtomicBoolean isAnimating = new AtomicBoolean(false); private static final AtomicBoolean isAnimating = new AtomicBoolean(false);
private static final AtomicBoolean isSpeaking = new AtomicBoolean(false); private static final AtomicBoolean isSpeaking = new AtomicBoolean(false);
private static final Locale DEFAULT_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS); private static final Locale DEFAULT_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS);
/** /**
* Speak a phrase with Pepper. * The latest QiContext used by Pepper.
* This can be publicly accessed to get the latest QiContext used by Pepper.
*/
public static QiContext latestContext = null;
/**
* Make the Pepper robot speak a phrase.
* This function adds a SpeechEvent to the event queue,
* and will speak the phrase once Pepper is ready to process it.
* *
* @param phrase The phrase to speak * @param phrase The phrase to speak
* @param context The QiContext to use
*/ */
public static void speak(String phrase, QiContext context) public static void speak(String phrase) {
{ addToEventQueue(new PepperSpeechEvent(phrase, DEFAULT_LOCALE));
addToEventQueue(new PepperSpeechEvent(context, phrase, DEFAULT_LOCALE));
} }
/** /**
* Animate Pepper with the given animation. * Make the Pepper robot perform an animation.
* This function adds an AnimationEvent to the event queue,
* and will perform the animation once Pepper is ready to process it.
* *
* @param animationName The name of the animation to play * @param animationName The name of the animation to play
* @param context The QiContext to use
*/ */
public static void animate(String animationName, QiContext context) public static void animate(String animationName) {
{ addToEventQueue(new PepperAnimationEvent(animationName));
addToEventQueue(new PepperAnimationEvent(context, animationName));
} }
/** /**
@@ -50,8 +64,7 @@ public class Pepper {
* *
* @param event The event to add to the queue * @param event The event to add to the queue
*/ */
public static void addToEventQueue(AbstractPepperActionEvent event) public static void addToEventQueue(AbstractPepperActionEvent event) {
{
pepperActionEventQueue.add(event); pepperActionEventQueue.add(event);
processEventQueue(); processEventQueue();
} }
@@ -63,25 +76,23 @@ public class Pepper {
* This prevents multiple actions from being executed at the same time, which can * This prevents multiple actions from being executed at the same time, which can
* cause the application to crash. * cause the application to crash.
*/ */
private static synchronized void processEventQueue() private static synchronized void processEventQueue() {
{ if (!pepperActionEventQueue.isEmpty()) {
if (!pepperActionEventQueue.isEmpty())
{
AbstractPepperActionEvent event = pepperActionEventQueue.poll(); AbstractPepperActionEvent event = pepperActionEventQueue.poll();
if (event == null) // Prevent null pointer exceptions
if (event == null || latestContext == null)
return; return;
switch (event.getAction()) switch (event.getAction()) {
{
/* Event for speaking a phrase **/ /* Event for speaking a phrase **/
case ACTION_SPEAK: case ACTION_SPEAK:
if ( !(event instanceof PepperSpeechEvent) || isSpeaking.get()) if (!(event instanceof PepperSpeechEvent) || isSpeaking.get())
break; break;
PepperSpeechEvent speechEvent = (PepperSpeechEvent) event; PepperSpeechEvent speechEvent = (PepperSpeechEvent) event;
isSpeaking.set(true); isSpeaking.set(true);
speechEvent speechEvent
.sayPhrase() .getSay(latestContext)
.async() .async()
.run() .run()
.andThenConsume(future -> { .andThenConsume(future -> {
@@ -90,13 +101,13 @@ public class Pepper {
}); });
break; break;
/* Event for animating the robot **/ /* Event for animating the robot **/
case ACTION_ANIMATE: case ACTION_ANIMATE:
if ( !(event instanceof PepperAnimationEvent) || isAnimating.get()) if (!(event instanceof PepperAnimationEvent) || isAnimating.get())
break; break;
PepperAnimationEvent animationEvent = (PepperAnimationEvent) event; PepperAnimationEvent animationEvent = (PepperAnimationEvent) event;
animationEvent animationEvent
.getAnimation() .getAnimation(latestContext)
.async() .async()
.run() .run()
.andThenConsume(future -> { .andThenConsume(future -> {
@@ -105,9 +116,27 @@ public class Pepper {
}); });
break; break;
default: default:
// Do nothing
break; break;
} }
} }
} }
/**
* Function for providing the latest QiContext to Pepper.
*
* @param context The QiContext to use.
* This can be null if the context is not available.
* @param origin The origin of the context.
* This parameter is to prevent other classes from
* unnecessarily providing a context, or setting
* the context to null.
*/
public static void provideContext(QiContext context, Class<? extends RobotLifecycleCallbacks> origin) {
latestContext = context;
if (context != null) {
processEventQueue();
}
}
} }

View File

@@ -9,10 +9,27 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
public class PepperAnimationEvent extends AbstractPepperActionEvent { public class PepperAnimationEvent extends AbstractPepperActionEvent {
public final String animationName; public final String animationName;
private final IAnimationCompletedListener onLabelReachedListener;
public PepperAnimationEvent(QiContext context, String animationName) {
super(context); /**
* Constructor for PepperAnimationEvent
*
* @param animationName The name of the animation to play
* @param onLabelReachedListener The listener to call when the animation is completed
*/
public PepperAnimationEvent(String animationName, IAnimationCompletedListener onLabelReachedListener) {
this.animationName = animationName; this.animationName = animationName;
this.onLabelReachedListener = onLabelReachedListener;
}
/**
* Constructor for PepperAnimationEvent
*
* @param animationName The name of the animation to play
*/
public PepperAnimationEvent(String animationName) {
this(animationName, null);
} }
@Override @Override
@@ -24,14 +41,25 @@ public class PepperAnimationEvent extends AbstractPepperActionEvent {
* Returns an animation object, which can be used to play the animation * Returns an animation object, which can be used to play the animation
* in the Pepper class. * in the Pepper class.
*/ */
public Animate getAnimation() public Animate getAnimation(QiContext context) {
{ Animation animation = AnimationBuilder.with(context)
Animation animation = AnimationBuilder.with(this.context) .withResources(context.getResources().getIdentifier(animationName, "raw", context.getPackageName()))
.withResources(this.context.getResources().getIdentifier(animationName, "raw", this.context.getPackageName()))
.build(); .build();
return AnimateBuilder.with(this.context) Animate animate = AnimateBuilder.with(context)
.withAnimation(animation) .withAnimation(animation)
.build(); .build();
// Add a listener for when a label is reached
animate.addOnLabelReachedListener((label, time) -> {
if (onLabelReachedListener != null && "end".equals(label)) {
onLabelReachedListener.onComplete();
}
});
return animate;
}
public interface IAnimationCompletedListener {
void onComplete();
} }
} }

View File

@@ -10,9 +10,7 @@ public class PepperSpeechEvent extends AbstractPepperActionEvent {
public final String phrase; public final String phrase;
public final Locale locale; public final Locale locale;
public PepperSpeechEvent(QiContext context, String phrase, Locale locale) public PepperSpeechEvent(String phrase, Locale locale) {
{
super(context);
this.locale = locale; this.locale = locale;
this.phrase = phrase; this.phrase = phrase;
} }
@@ -25,9 +23,8 @@ public class PepperSpeechEvent extends AbstractPepperActionEvent {
/** /**
* Returns a Say object, which can then be executed. * Returns a Say object, which can then be executed.
*/ */
public Say sayPhrase() public Say getSay(QiContext context) {
{ return SayBuilder.with(context)
return SayBuilder.with(this.context)
.withText(this.phrase) .withText(this.phrase)
.withLocale(this.locale) .withLocale(this.locale)
.build(); .build();

View File

@@ -1,6 +1,9 @@
package com.example.fitbot.ui.activities; package com.example.fitbot.ui.activities;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.widget.VideoView; import android.widget.VideoView;
import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.QiContext;
@@ -12,9 +15,8 @@ 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.pepper.Pepper;
import com.example.fitbot.ui.components.PersonalMotionPreviewElement; import com.example.fitbot.ui.components.ExerciseStatusElement;
import com.example.fitbot.util.NavigationManager; import com.example.fitbot.util.NavigationManager;
import com.example.fitbot.util.FitnessCycle;
import com.example.fitbot.util.processing.InputProcessor; import com.example.fitbot.util.processing.InputProcessor;
import org.joml.Vector3f; import org.joml.Vector3f;
@@ -22,14 +24,12 @@ import org.joml.Vector3f;
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks { public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
// Private fields for the FitnessActivity class. // Private fields for the FitnessActivity class.
private PersonalMotionPreviewElement personalMotionPreviewElement; private ExerciseStatusElement personalMotionPreviewElement;
private InputProcessor motionProcessor; private InputProcessor motionProcessor;
private Exercise currentExercise; private Exercise currentExercise;
private QiContext qiContext;
// Some nice little messages for the user // Some nice little messages for the user
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.",
", de oefeningen slapen nog, probeer het later nog eens." ", de oefeningen slapen nog, probeer het later nog eens."
@@ -39,7 +39,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
"Indien dit probleem zich voortzet, neem contact op met de ontwikkelaar."; "Indien dit probleem zich voortzet, neem contact op met de ontwikkelaar.";
private static final float SENSOR_SAMPLE_RATE = 10.0f; private static final float SENSOR_SAMPLE_RATE = 10.0f;
private static final int EXERCISE_COUNT = 5; private static final int EXERCISE_COUNT = 5;
private static final float EXERCISE_SPEED_MULTIPLIER = 1.0f; private static final float EXERCISE_SPEED_MULTIPLIER = 1.0f;
@Override @Override
@@ -54,17 +54,16 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
// Find the VideoView by its ID // Find the VideoView by its ID
VideoView videoView = findViewById(R.id.videoView); VideoView videoView = findViewById(R.id.videoView);
FitnessCycle.playVideo(videoView, this); playVideo(videoView, this);
NavigationManager.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class); NavigationManager.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
NavigationManager.hideSystemUI(this); NavigationManager.hideSystemUI(this);
} }
@Override @Override
public void onRobotFocusGained(QiContext qiContext) { public void onRobotFocusGained(QiContext qiContext) {
this.qiContext = qiContext;
// Find the VideoView by its ID // Provide the context so that all queued actions can be performed.
// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext)); Pepper.provideContext(qiContext, this.getClass());
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement); personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
@@ -74,19 +73,18 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
personalMotionPreviewElement.post(() -> { personalMotionPreviewElement.post(() -> {
this.acquireExercise((exercise) -> { this.acquireExercise((exercise) -> {
// Acquire paths from the exercise and provide them to the motion processor // Acquire paths from the exercise and provide them to the motion processor
Vector3f[][] vectors = new Vector3f[][] {exercise.leftPath.getVectors(), exercise.rightPath.getVectors()}; Vector3f[][] vectors = new Vector3f[][]{exercise.leftPath.getVectors(), exercise.rightPath.getVectors()};
motionProcessor = new InputProcessor(vectors, exercise.exerciseTimeInSeconds, SENSOR_SAMPLE_RATE); motionProcessor = new InputProcessor(vectors, exercise.exerciseTimeInSeconds, SENSOR_SAMPLE_RATE);
personalMotionPreviewElement.provideQiContext(qiContext);
personalMotionPreviewElement.initialize(exercise, motionProcessor, EXERCISE_COUNT); personalMotionPreviewElement.initialize(exercise, motionProcessor, EXERCISE_COUNT);
motionProcessor.startListening(); motionProcessor.startListening();
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);
Pepper.speak(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext); Pepper.speak(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex]);
Pepper.speak(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, qiContext); Pepper.speak(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE);
NavigationManager.navigateToActivity(this, MainActivity.class); NavigationManager.navigateToActivity(this, MainActivity.class);
}); });
}); });
@@ -104,33 +102,59 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
// therefore we'll have to do it like this... // therefore we'll have to do it like this...
(new Thread(() -> { (new Thread(() -> {
Exercise exercise = ExerciseManager.retrieveExercise(); Exercise exercise = ExerciseManager.retrieveExercise();
if ( exercise == null) if (exercise == null) {
{
onFailedFetch.handle(null); onFailedFetch.handle(null);
} } else {
else {
onSuccessfulFetch.handle(exercise); onSuccessfulFetch.handle(exercise);
} }
})).start(); })).start();
} }
/**
* Function for playing a video in a VideoView
*
* @param videoView The VideoView to play the video in
* @param context The context to use
*/
public static void playVideo(VideoView videoView, Context context) {
// Set up the video player
if (videoView != null) {
Uri videoUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.bicepvideo);
videoView.setVideoURI(videoUri);
videoView.setOnCompletionListener(mp -> {
// Repeat the video when it finishes playing
videoView.start();
});
videoView.start();
} else {
Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
}
}
@Override @Override
public void onRobotFocusLost() { public void onRobotFocusLost() {
QiSDK.unregister(this, this); QiSDK.unregister(this, this);
Pepper.provideContext(null, this.getClass()); // Remove the context (unavailable)
// Go to the main screen // Go to the main screen
NavigationManager.navigateToActivity(this, MainActivity.class); NavigationManager.navigateToActivity(this, MainActivity.class);
} }
@Override @Override
public void onRobotFocusRefused(String reason) {} public void onRobotFocusRefused(String reason) {
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
if ( this.motionProcessor != null ) { if (this.motionProcessor != null) {
this.motionProcessor.stopListening(); this.motionProcessor.stopListening();
this.motionProcessor = null; this.motionProcessor = null;
} }
QiSDK.unregister(this, this); QiSDK.unregister(this, this);
Pepper.provideContext(null, this.getClass());
super.onDestroy(); super.onDestroy();
} }
} }

View File

@@ -1,9 +1,7 @@
package com.example.fitbot.ui.activities; package com.example.fitbot.ui.activities;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.support.v7.app.AppCompatActivity;
import com.example.fitbot.R; import com.example.fitbot.R;
import com.example.fitbot.util.NavigationManager; import com.example.fitbot.util.NavigationManager;

View File

@@ -9,13 +9,12 @@ import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.aldebaran.qi.sdk.QiContext;
import com.example.fitbot.exercise.Exercise; import com.example.fitbot.exercise.Exercise;
import com.example.fitbot.pepper.Pepper;
import com.example.fitbot.ui.activities.EndScreenActivity; import com.example.fitbot.ui.activities.EndScreenActivity;
import com.example.fitbot.ui.activities.FitnessActivity; import com.example.fitbot.ui.activities.FitnessActivity;
import com.example.fitbot.ui.activities.MainActivity; import com.example.fitbot.ui.activities.MainActivity;
import com.example.fitbot.util.NavigationManager; import com.example.fitbot.util.NavigationManager;
import com.example.fitbot.util.FitnessCycle;
import com.example.fitbot.util.processing.IInputHandler; import com.example.fitbot.util.processing.IInputHandler;
import com.example.fitbot.util.processing.InputProcessor; import com.example.fitbot.util.processing.InputProcessor;
@@ -26,12 +25,11 @@ import org.joml.Vector4f;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
public class PersonalMotionPreviewElement extends View implements IInputHandler { public class ExerciseStatusElement extends View implements IInputHandler {
// Fields regarding Exercise and speech handling. // Fields regarding Exercise and speech handling.
private InputProcessor motionProcessor; private InputProcessor motionProcessor;
private Exercise exercise; private Exercise exercise;
private QiContext qiContext;
private int exerciseCount; private int exerciseCount;
private FitnessActivity parentActivity; private FitnessActivity parentActivity;
@@ -41,10 +39,10 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
private final Paint backgroundPaint = new Paint(); private final Paint backgroundPaint = new Paint();
// TODO: Remove // TODO: Remove
private Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation. private final Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation.
private Matrix4f projectionMatrix = new Matrix4f(); // The projection matrix for the 3D to 2D transformation. private final Matrix4f projectionMatrix = new Matrix4f(); // The projection matrix for the 3D to 2D transformation.
private final Vector4f objectPosition = new Vector4f(0, 0, 0, 1); // The location of the object in 3D space. private final Vector4f objectPosition = new Vector4f(0, 0, 0, 1); // The location of the object in 3D space.
private ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>(); private final ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
// TODO: Remove // TODO: Remove
private Vector2f[] axisVectors = new Vector2f[0]; private Vector2f[] axisVectors = new Vector2f[0];
@@ -57,11 +55,10 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
}; };
public PersonalMotionPreviewElement(Context context, AttributeSet attrs) { public ExerciseStatusElement(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
if ( context instanceof Activity) if (context instanceof Activity) {
{
this.parentActivity = (FitnessActivity) context; this.parentActivity = (FitnessActivity) context;
} }
@@ -85,9 +82,9 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
* created, otherwise the dimensions of the element aren't initialized yet, which * created, otherwise the dimensions of the element aren't initialized yet, which
* will cause the vertex projections to fail (0 width and height). * will cause the vertex projections to fail (0 width and height).
* *
* @param exercise The exercise that the user is currently performing. * @param exercise The exercise that the user is currently performing.
* @param motionProcessor The motion processor that will be used to process the user's motion. * @param motionProcessor The motion processor that will be used to process the user's motion.
* @param exerciseCount The total amount of exercises that the user has to perform. * @param exerciseCount The total amount of exercises that the user has to perform.
*/ */
public void initialize(@Nullable Exercise exercise, InputProcessor motionProcessor, int exerciseCount) { public void initialize(@Nullable Exercise exercise, InputProcessor motionProcessor, int exerciseCount) {
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement."); Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
@@ -96,8 +93,8 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
this.exercise = exercise; this.exercise = exercise;
this.exerciseCount = exerciseCount; this.exerciseCount = exerciseCount;
// TODO: Remove /* TODO: Remove */
this.axisVectors = new Vector2f[] { this.axisVectors = new Vector2f[]{
projectVertex(new Vector3f(-5.0f, 0, 0), getWidth(), getHeight()), projectVertex(new Vector3f(-5.0f, 0, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(5.0f, 0, 0), getWidth(), getHeight()), projectVertex(new Vector3f(5.0f, 0, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(0, -5.0f, 0), getWidth(), getHeight()), projectVertex(new Vector3f(0, -5.0f, 0), getWidth(), getHeight()),
@@ -106,25 +103,24 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
projectVertex(new Vector3f(0, 0, 5.0f), getWidth(), getHeight()) projectVertex(new Vector3f(0, 0, 5.0f), getWidth(), getHeight())
}; };
Pepper.speak(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]);
// Handler that is called every time the motion processor receives new data. // Handler that is called every time the motion processor receives new data.
this.motionProcessor.setInputHandler((rotationVector, deviceId) -> { this.motionProcessor.setInputHandler((rotationVector, deviceId) -> {
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector); Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed())); Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed()));
if ( this.motionProcessor.hasFinished()) if (this.motionProcessor.hasFinished()) {
{
// If for some reason the parent activity is not defined, // If for some reason the parent activity is not defined,
// move back to the main screen. // move back to the main screen.
if ( this.parentActivity == null) if (this.parentActivity == null) {
{
// Move to main screen // Move to main screen
NavigationManager.navigateToActivity(getContext(), MainActivity.class); NavigationManager.navigateToActivity(getContext(), MainActivity.class);
return; return;
} }
// Move on to the next exercise, or finish. // Move on to the next exercise, or finish.
if ( this.exerciseCount > 0 ) if (this.exerciseCount > 0) {
{
this.exerciseCount--; this.exerciseCount--;
this.parentActivity.acquireExercise((newExercise) -> { this.parentActivity.acquireExercise((newExercise) -> {
this.motionProcessor.useExercise(newExercise); this.motionProcessor.useExercise(newExercise);
@@ -132,43 +128,27 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
// Move to main screen // Move to main screen
NavigationManager.navigateToActivity(parentActivity, MainActivity.class); NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
}); });
} } else {
else
{
// Finish the exercise. // Finish the exercise.
NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class); NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
return; return;
} }
} }
// TODO: Adjust / remove /* TODO: Use raw vector */
vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight())); vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight()));
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
/* TODO: Remove */
Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight()); Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight());
this.vectors.add(parsed); this.vectors.add(parsed /* TODO: Add regular vertex once exercise data is stored in DB*/);
// Remove the first element if the array is too big // Remove the first element if the array is too big
if (this.vectors.size() > 100) if (this.vectors.size() > 100)
this.vectors.poll(); this.vectors.poll();
}); });
} }
/**
* Function for providing a QiContext to the PersonalMotionPreviewElement.
* This function will be called by the parent activity when the QiContext is available.
* Also say something nice to the user :)
*
* @param context The QiContext to provide.
*/
public void provideQiContext(QiContext context) {
this.qiContext = context;
if (this.qiContext == null)
return;
FitnessCycle.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)], this.qiContext);
}
/** /**
* Method for setting the gesture path that will be drawn on the canvas. * Method for setting the gesture path that will be drawn on the canvas.
* *
@@ -203,7 +183,6 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
} }
@Override @Override
public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint); canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
@@ -211,17 +190,17 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
/*if (this.exercise == null) /*if (this.exercise == null)
return;*/ return;*/
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length/2; i++)
{ /* TODO: Remove */
startX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2].x)); for (int i = 0, startX, endX, startY, endY; i < axisVectors.length / 2; i++) {
endX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2+1].x)); startX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2].x));
startY = (int)Math.max(0, Math.min(this.getHeight(), (int)axisVectors[i*2].y)); endX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2 + 1].x));
endY = (int)Math.max(0, Math.min(this.getHeight(), (int)axisVectors[i*2+1].y)); startY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2].y));
endY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2 + 1].y));
canvas.drawLine(startX, startY, endX, endY, this.borderPaint); canvas.drawLine(startX, startY, endX, endY, this.borderPaint);
} }
for ( Vector2f point : this.vectors) for (Vector2f point : this.vectors) {
{
canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint); canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint);
} }
/* /*

View File

@@ -11,8 +11,7 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
public class Animations { public class Animations {
public static void Animate(String AnimationFile, QiContext ctx) public static void Animate(String AnimationFile, QiContext ctx) {
{
int resId = ctx.getResources().getIdentifier(AnimationFile, "raw", ctx.getPackageName()); int resId = ctx.getResources().getIdentifier(AnimationFile, "raw", ctx.getPackageName());
Animation animation = AnimationBuilder.with(ctx) Animation animation = AnimationBuilder.with(ctx)

View File

@@ -1,164 +0,0 @@
package com.example.fitbot.util;
import android.content.Context;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.VideoView;
import com.aldebaran.qi.sdk.builder.SayBuilder;
import com.aldebaran.qi.sdk.object.conversation.Phrase;
import com.aldebaran.qi.sdk.object.conversation.Say;
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 com.example.fitbot.R;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class FitnessCycle extends AppCompatActivity {
private static final Locale DUTCH_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS);
private static final BlockingQueue<PepperSpeechEntry> MESSAGE_QUEUE = new LinkedBlockingQueue<>();
private static boolean SPEECH_ACTIVE = false;
/**
* Function for executing a movement animation a certain number of times
* on the robot
*
* @param Exercise The name of the exercise to perform
* @param Reps The number of repetitions to perform
* @param qiContext The QiContext to use
*/
public static void executeMovement(String Exercise, int Reps, QiContext qiContext) {
AtomicInteger repCount = new AtomicInteger(0);
Animation animation = AnimationBuilder.with(qiContext)
.withResources(qiContext.getResources().getIdentifier(Exercise, "raw", qiContext.getPackageName()))
.build();
Animate animate = AnimateBuilder.with(qiContext)
.withAnimation(animation)
.build();
// Add a listener for when a label is reached
animate.addOnLabelReachedListener((label, time) -> {
// Increment repCount when the end of a repetition is reached
if ("end".equals(label)) {
repCount.incrementAndGet();
}
});
// Run the animation the desired number of times
for (int i = 0; i < Reps; i++) {
animate.run();
}
}
/**
* Function for making the robot say something with DUTCH_LOCALE as locale
*
* @param phrase The phrase to make the robot say
* @param ctx The QiContext to use
*/
public static void say(String phrase, QiContext ctx) {
say(phrase, ctx, DUTCH_LOCALE);
}
/**
* Function for making the robot say something with a specific locale
*
* @param phrase The phrase to make the robot say
* @param ctx The QiContext to use
* @param locale The locale to use
*/
public static void say(String phrase, QiContext ctx, Locale locale) {
// If the robot is already speaking, add the phrase to the queue
MESSAGE_QUEUE.add(new PepperSpeechEntry(phrase, ctx, locale));
processMessageQueue();
}
private static synchronized void processMessageQueue() {
if (SPEECH_ACTIVE)
return;
PepperSpeechEntry nextPhrase = MESSAGE_QUEUE.poll();
if (nextPhrase != null) {
SPEECH_ACTIVE = true;
Say say = nextPhrase.getSay();
say.async().run().andThenConsume(future -> {
SPEECH_ACTIVE = false;
processMessageQueue(); // Process next item in the queue
});
}
}
/**
* Function for playing a video in a VideoView
*
* @param videoView The VideoView to play the video in
* @param context The context to use
*/
public static void playVideo(VideoView videoView, Context context) {
// Set up the video player
if (videoView != null) {
Uri videoUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.bicepvideo);
videoView.setVideoURI(videoUri);
videoView.setOnCompletionListener(mp -> {
// Repeat the video when it finishes playing
videoView.start();
});
videoView.start();
} else {
Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
}
}
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;
}
public Say getSay() {
return SayBuilder
.with(context)
.withLocale(locale)
.withText(phrase)
.build();
}
}
private enum PepperAction {
SPEAK,
ANIMATE,
MOVE
}
private static class PepperActionEvent {
public PepperAction action;
public Object data;
}
}

View File

@@ -6,23 +6,18 @@ import android.content.Intent;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import com.example.fitbot.ui.activities.FitnessActivity;
import com.example.fitbot.ui.activities.MainActivity;
import com.example.fitbot.ui.activities.HelpActivity;
public class NavigationManager { public class NavigationManager {
/** /**
* Sets up a button to navigate to a different activity when clicked. * Sets up a button to navigate to a different activity when clicked.
* *
* @param currentActivity The activity that contains the button * @param currentActivity The activity that contains the button
* @param buttonId The ID of the button * @param buttonId The ID of the button
* @param targetActivity The activity to navigate to * @param targetActivity The activity to navigate to
*/ */
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);
if ( button == null ) if (button == null) {
{
throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName()); throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName());
} }
button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity)); button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity));
@@ -32,7 +27,7 @@ public class NavigationManager {
* Navigates to the target activity. * Navigates to the target activity.
* *
* @param currentActivity The current activity * @param currentActivity The current activity
* @param targetActivity The target activity * @param targetActivity The target activity
*/ */
public static void navigateToActivity(Activity currentActivity, Class<? extends Activity> targetActivity) { public static void navigateToActivity(Activity currentActivity, Class<? extends Activity> targetActivity) {
Intent intent = new Intent(currentActivity, targetActivity); Intent intent = new Intent(currentActivity, targetActivity);
@@ -45,7 +40,7 @@ public class NavigationManager {
/** /**
* Navigates to the target activity. * Navigates to the target activity.
* *
* @param context The context * @param context The context
* @param targetActivity The target activity * @param targetActivity The target activity
*/ */
public static void navigateToActivity(Context context, Class<? extends Activity> targetActivity) { public static void navigateToActivity(Context context, Class<? extends Activity> targetActivity) {

View File

@@ -2,10 +2,6 @@ package com.example.fitbot.util.path;
import org.joml.Vector3f; import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class GesturePath { public class GesturePath {
// The vectors that make up the path. // The vectors that make up the path.
@@ -16,18 +12,18 @@ public class GesturePath {
* *
* @param vectors The vectors that make up the path. * @param vectors The vectors that make up the path.
*/ */
public GesturePath(Vector3f[] vectors) public GesturePath(Vector3f[] vectors) {
{ if (vectors.length < 2)
if ( vectors.length < 2)
throw new IllegalArgumentException("A path must have at least two points."); throw new IllegalArgumentException("A path must have at least two points.");
this.segments = new PathSegment[vectors.length - 1]; this.segments = new PathSegment[vectors.length - 1];
for ( int i = 0; i < vectors.length - 1; i++) for (int i = 0; i < vectors.length - 1; i++)
segments[i] = new PathSegment(vectors[i], vectors[i + 1]); segments[i] = new PathSegment(vectors[i], vectors[i + 1]);
} }
/** /**
* Constructor for a GesturePath with provided PathSegments. * Constructor for a GesturePath with provided PathSegments.
*
* @param segments The PathSegments to initialize the path with. * @param segments The PathSegments to initialize the path with.
*/ */
public GesturePath(PathSegment... segments) { public GesturePath(PathSegment... segments) {
@@ -46,11 +42,10 @@ public class GesturePath {
/** /**
* Method for retrieving the vectors of the GesturePath. * Method for retrieving the vectors of the GesturePath.
*/ */
public Vector3f[] getVectors() public Vector3f[] getVectors() {
{
Vector3f[] vectors = new Vector3f[segments.length + 1]; Vector3f[] vectors = new Vector3f[segments.length + 1];
vectors[0] = segments[0].getStart(); vectors[0] = segments[0].getStart();
for ( int i = 0; i < segments.length; i++) for (int i = 0; i < segments.length; i++)
vectors[i + 1] = segments[i].getEnd(); vectors[i + 1] = segments[i].getEnd();
return vectors; return vectors;
@@ -64,11 +59,11 @@ public class GesturePath {
*/ */
public PathSegment closest(Vector3f reference) { public PathSegment closest(Vector3f reference) {
// If there's only one segment, return that one. // If there's only one segment, return that one.
if ( segments.length == 1) if (segments.length == 1)
return segments[0]; return segments[0];
PathSegment closest = segments[0]; PathSegment closest = segments[0];
for ( int i = 1; i < segments.length; i++) for (int i = 1; i < segments.length; i++)
closest = PathSegment.closer(closest, segments[i], reference); closest = PathSegment.closer(closest, segments[i], reference);
return closest; return closest;
@@ -89,7 +84,7 @@ public class GesturePath {
* Function for converting a string to a GesturePath object. * Function for converting a string to a GesturePath object.
* The input string bytes will be directly converted into 3d vectors. * The input string bytes will be directly converted into 3d vectors.
* Every scalar is composed of 32 bits (4 characters), meaning 96 bits per vector. * Every scalar is composed of 32 bits (4 characters), meaning 96 bits per vector.
* * <p>
* Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first). * Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first).
* *
* @param input The string to convert * @param input The string to convert

View File

@@ -12,7 +12,7 @@ public class PathSegment {
* pointing straight upwards relative to the line, with a curvature of 0.0. * pointing straight upwards relative to the line, with a curvature of 0.0.
* *
* @param start The starting point of the line segment * @param start The starting point of the line segment
* @param end The end point of the line segment. * @param end The end point of the line segment.
*/ */
public PathSegment(Vector3f start, Vector3f end) { public PathSegment(Vector3f start, Vector3f end) {
this.start = start; this.start = start;
@@ -25,7 +25,7 @@ public class PathSegment {
* depending on the curvature of the curve. If the curvature is unset, or set to 0 * depending on the curvature of the curve. If the curvature is unset, or set to 0
* then this method will use linear interpolation. Otherwise, it will use a curve. * then this method will use linear interpolation. Otherwise, it will use a curve.
* *
* @param t The interpolation value between 0 and 1. * @param t The interpolation value between 0 and 1.
*/ */
public Vector3f interpolate(double t) { public Vector3f interpolate(double t) {
return new Vector3f(this.start) return new Vector3f(this.start)
@@ -56,7 +56,7 @@ public class PathSegment {
(float) (this.start.x + t * (this.end.x - this.start.x)), (float) (this.start.x + t * (this.end.x - this.start.x)),
(float) (this.start.y + t * (this.end.y - this.start.y)), (float) (this.start.y + t * (this.end.y - this.start.y)),
(float) (this.start.z + t * (this.end.z - this.start.z)) (float) (this.start.z + t * (this.end.z - this.start.z))
)); ));
} }
/** /**
@@ -84,7 +84,7 @@ public class PathSegment {
* @return The distance to the closest point on the path segment. * @return The distance to the closest point on the path segment.
*/ */
public double distance(Vector3f reference) { public double distance(Vector3f reference) {
if ( this.start.distanceSquared(reference) > this.end.distanceSquared(reference)) if (this.start.distanceSquared(reference) > this.end.distanceSquared(reference))
return this.end.distance(reference); return this.end.distance(reference);
return this.start.distance(reference); return this.start.distance(reference);
} }
@@ -92,8 +92,8 @@ public class PathSegment {
/** /**
* Function for returning the closest path segment to a reference point. * Function for returning the closest path segment to a reference point.
* *
* @param first The first path segment to compare. * @param first The first path segment to compare.
* @param second The second path segment to compare. * @param second The second path segment to compare.
* @param referencePoint The reference point to compare to. * @param referencePoint The reference point to compare to.
* @return The closest path segment to the reference point. * @return The closest path segment to the reference point.
*/ */

View File

@@ -4,11 +4,12 @@ import org.joml.Vector3f;
public interface IInputHandler { public interface IInputHandler {
/** /**
* Function for accepting motion data and the transformed vector. * Function for accepting motion data and the transformed vector.
* @param rotationVector The rotation vector of the motion data. *
* @param sensorId The sensor ID of the motion data. * @param rotationVector The rotation vector of the motion data.
*/ * @param sensorId The sensor ID of the motion data.
void accept(Vector3f rotationVector, int sensorId); */
void accept(Vector3f rotationVector, int sensorId);
} }

View File

@@ -49,6 +49,7 @@ public class InputProcessor {
* Function for setting the exercise to use. * Function for setting the exercise to use.
* This updates the user and target path and the * This updates the user and target path and the
* duration of the exercise. * duration of the exercise.
*
* @param exercise The exercise to use the paths for. * @param exercise The exercise to use the paths for.
*/ */
public void useExercise(Exercise exercise) { public void useExercise(Exercise exercise) {
@@ -61,6 +62,7 @@ public class InputProcessor {
/** /**
* Function for checking if the exercise has finished. * Function for checking if the exercise has finished.
*
* @return True if the exercise has finished, false otherwise. * @return True if the exercise has finished, false otherwise.
*/ */
public boolean hasFinished() { public boolean hasFinished() {
@@ -171,8 +173,7 @@ public class InputProcessor {
* *
* @return The current progress of the exercise. * @return The current progress of the exercise.
*/ */
public double getCurrentProgress() public double getCurrentProgress() {
{
return secondsPassed / exerciseDuration; return secondsPassed / exerciseDuration;
} }
@@ -208,8 +209,7 @@ public class InputProcessor {
// Ensure the indexes are within the bounds of the array // Ensure the indexes are within the bounds of the array
if (targetIndex >= 0 && targetIndex <= this.targetRotationVectorPaths[sensorId].length - 1 && if (targetIndex >= 0 && targetIndex <= this.targetRotationVectorPaths[sensorId].length - 1 &&
selfIndex >= 0 && selfIndex <= this.selfRotationVectorPaths[sensorId].length - 1) selfIndex >= 0 && selfIndex <= this.selfRotationVectorPaths[sensorId].length - 1) {
{
return this.selfRotationVectorPaths[sensorId][selfIndex].distance(this.targetRotationVectorPaths[sensorId][targetIndex]); return this.selfRotationVectorPaths[sensorId][selfIndex].distance(this.targetRotationVectorPaths[sensorId][targetIndex]);
} }
return 0.0d; return 0.0d;

View File

@@ -1,7 +1,5 @@
package com.example.fitbot.util.server; package com.example.fitbot.util.server;
import java.net.Socket;
/** /**
* Interface for handling WebSocket events. * Interface for handling WebSocket events.
*/ */

View File

@@ -10,16 +10,13 @@ import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
public class WebServer implements Runnable { public class WebServer implements Runnable {
private ServerSocket serverSocket; private ServerSocket serverSocket;
protected IWebServerHandler eventHandler = (input) -> {}; // No-op. protected IWebServerHandler eventHandler = (input) -> {
}; // No-op.
private Thread thread; private Thread thread;
private final AtomicBoolean forceClose = new AtomicBoolean(false); private final AtomicBoolean forceClose = new AtomicBoolean(false);
@@ -84,7 +81,7 @@ public class WebServer implements Runnable {
String[] data = builder.toString().split("\n\n"); String[] data = builder.toString().split("\n\n");
if ( data.length > 1) { // Check if the data is valid. if (data.length > 1) { // Check if the data is valid.
this.eventHandler.onReceive(data[1]); this.eventHandler.onReceive(data[1]);
} }

View File

@@ -56,7 +56,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<com.example.fitbot.ui.components.PersonalMotionPreviewElement <com.example.fitbot.ui.components.ExerciseStatusElement
android:id="@+id/personalMotionPreviewElement" android:id="@+id/personalMotionPreviewElement"
android:layout_width="350dp" android:layout_width="350dp"
android:layout_height="350dp" /> android:layout_height="350dp" />