Removed FitnessCycle, reformatted code, made Pepper actions globally accessible
This commit is contained in:
@@ -19,11 +19,10 @@ public enum EMuscleGroup {
|
||||
return this.muscleGroupIdentifier;
|
||||
}
|
||||
|
||||
public static EMuscleGroup parse(String name)
|
||||
{
|
||||
for ( EMuscleGroup muscleGroup : EMuscleGroup.values())
|
||||
for ( String muscleGroupName : muscleGroup.muscleGroupNames)
|
||||
if ( muscleGroupName.equalsIgnoreCase(name))
|
||||
public static EMuscleGroup parse(String name) {
|
||||
for (EMuscleGroup muscleGroup : EMuscleGroup.values())
|
||||
for (String muscleGroupName : muscleGroup.muscleGroupNames)
|
||||
if (muscleGroupName.equalsIgnoreCase(name))
|
||||
return muscleGroup;
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,12 +1,6 @@
|
||||
package com.example.fitbot.exercise;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
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 {
|
||||
|
||||
@@ -22,13 +16,13 @@ public class Exercise {
|
||||
/**
|
||||
* Constructor for the AbstractExercise class.
|
||||
*
|
||||
* @param muscleGroup The muscle group of the exercise.
|
||||
* @param leftPath The path of the left hand.
|
||||
* @param rightPath The path of the right hand.
|
||||
* @param title The title of the exercise.
|
||||
* @param description The description of the exercise.
|
||||
* @param imageUrl The URL of the image.
|
||||
* @param videoUrl The URL of the video.
|
||||
* @param muscleGroup The muscle group of the exercise.
|
||||
* @param leftPath The path of the left hand.
|
||||
* @param rightPath The path of the right hand.
|
||||
* @param title The title of the exercise.
|
||||
* @param description The description of the exercise.
|
||||
* @param imageUrl The URL of the image.
|
||||
* @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) {
|
||||
this.muscleGroup = muscleGroup;
|
||||
|
@@ -18,15 +18,15 @@ public class ExerciseManager {
|
||||
// The value of these property variables must be equivalent of
|
||||
// the JSON data that the database sends back.
|
||||
// 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_ID = "exerciseId";
|
||||
private static final String PROPERTY_MUSCLE_GROUP = "muscleGroup";
|
||||
private static final String PROPERTY_SHORT_DESC = "shortDescription";
|
||||
private static final String PROPERTY_IMAGE_URL = "imageUrl";
|
||||
private static final String PROPERTY_VIDEO_URL = "videoUrl";
|
||||
private static final String PROPERTY_DESC = "description";
|
||||
private static final String PROPERTY_PATH = "path";
|
||||
private static final String PROPERTY_NAME = "name";
|
||||
private static final String PROPERTY_EXERCISE_DURATION = "duration";
|
||||
private static final String PROPERTY_EXERCISE_ID = "exerciseId";
|
||||
private static final String PROPERTY_MUSCLE_GROUP = "muscleGroup";
|
||||
private static final String PROPERTY_SHORT_DESC = "shortDescription";
|
||||
private static final String PROPERTY_IMAGE_URL = "imageUrl";
|
||||
private static final String PROPERTY_VIDEO_URL = "videoUrl";
|
||||
private static final String PROPERTY_DESC = "description";
|
||||
private static final String PROPERTY_PATH = "path";
|
||||
private static final String PROPERTY_NAME = "name";
|
||||
|
||||
public static final int SENSOR_COUNT = 2;
|
||||
|
||||
@@ -37,18 +37,17 @@ public class ExerciseManager {
|
||||
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 EXERCISE_ERROR_MARGIN = 1.0f;
|
||||
|
||||
/**
|
||||
* Function for sending an HTTP request to the server.
|
||||
*
|
||||
* @param url The URL to send the request to.
|
||||
* @param method The method to use for the request, e.g. GET or POST.
|
||||
* @param url The URL to send the request to.
|
||||
* @param method The method to use for the request, e.g. GET or POST.
|
||||
* @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.
|
||||
*/
|
||||
public static String sendHTTP(String url, String method, String contentType, String body) {
|
||||
@@ -61,7 +60,7 @@ public class ExerciseManager {
|
||||
connection.connect();
|
||||
// Send a body if it is present
|
||||
if (body != null)
|
||||
connection.getOutputStream().write(body.getBytes());
|
||||
connection.getOutputStream().write(body.getBytes());
|
||||
InputStream stream = connection.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -102,7 +101,7 @@ public class ExerciseManager {
|
||||
// class to support more paths.
|
||||
String[] leftRightData = content.get(PROPERTY_PATH).getAsString().split(";");
|
||||
|
||||
if ( leftRightData.length != SENSOR_COUNT)
|
||||
if (leftRightData.length != SENSOR_COUNT)
|
||||
return null;
|
||||
|
||||
return new Exercise(
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -1,15 +1,6 @@
|
||||
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();
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@ package com.example.fitbot.pepper;
|
||||
public enum EPepperAction {
|
||||
ACTION_SPEAK,
|
||||
ACTION_ANIMATE,
|
||||
NO_ACTION;
|
||||
NO_ACTION
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
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.Locale;
|
||||
import com.aldebaran.qi.sdk.object.locale.Region;
|
||||
@@ -11,37 +12,50 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
/**
|
||||
* Class representing the Pepper robot.
|
||||
* 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 {
|
||||
|
||||
// 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 =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
private static final AtomicBoolean isAnimating = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean isSpeaking = new AtomicBoolean(false);
|
||||
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.
|
||||
* 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 context The QiContext to use
|
||||
*/
|
||||
public static void speak(String phrase, QiContext context)
|
||||
{
|
||||
addToEventQueue(new PepperSpeechEvent(context, phrase, DEFAULT_LOCALE));
|
||||
public static void speak(String phrase) {
|
||||
addToEventQueue(new PepperSpeechEvent(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 context The QiContext to use
|
||||
*/
|
||||
public static void animate(String animationName, QiContext context)
|
||||
{
|
||||
addToEventQueue(new PepperAnimationEvent(context, animationName));
|
||||
public static void animate(String animationName) {
|
||||
addToEventQueue(new PepperAnimationEvent(animationName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,8 +64,7 @@ public class Pepper {
|
||||
*
|
||||
* @param event The event to add to the queue
|
||||
*/
|
||||
public static void addToEventQueue(AbstractPepperActionEvent event)
|
||||
{
|
||||
public static void addToEventQueue(AbstractPepperActionEvent event) {
|
||||
pepperActionEventQueue.add(event);
|
||||
processEventQueue();
|
||||
}
|
||||
@@ -63,25 +76,23 @@ public class Pepper {
|
||||
* 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())
|
||||
{
|
||||
private static synchronized void processEventQueue() {
|
||||
if (!pepperActionEventQueue.isEmpty()) {
|
||||
AbstractPepperActionEvent event = pepperActionEventQueue.poll();
|
||||
|
||||
if (event == null)
|
||||
// Prevent null pointer exceptions
|
||||
if (event == null || latestContext == null)
|
||||
return;
|
||||
|
||||
switch (event.getAction())
|
||||
{
|
||||
switch (event.getAction()) {
|
||||
/* Event for speaking a phrase **/
|
||||
case ACTION_SPEAK:
|
||||
if ( !(event instanceof PepperSpeechEvent) || isSpeaking.get())
|
||||
if (!(event instanceof PepperSpeechEvent) || isSpeaking.get())
|
||||
break;
|
||||
PepperSpeechEvent speechEvent = (PepperSpeechEvent) event;
|
||||
isSpeaking.set(true);
|
||||
speechEvent
|
||||
.sayPhrase()
|
||||
.getSay(latestContext)
|
||||
.async()
|
||||
.run()
|
||||
.andThenConsume(future -> {
|
||||
@@ -90,13 +101,13 @@ public class Pepper {
|
||||
});
|
||||
break;
|
||||
|
||||
/* Event for animating the robot **/
|
||||
/* Event for animating the robot **/
|
||||
case ACTION_ANIMATE:
|
||||
if ( !(event instanceof PepperAnimationEvent) || isAnimating.get())
|
||||
if (!(event instanceof PepperAnimationEvent) || isAnimating.get())
|
||||
break;
|
||||
PepperAnimationEvent animationEvent = (PepperAnimationEvent) event;
|
||||
animationEvent
|
||||
.getAnimation()
|
||||
.getAnimation(latestContext)
|
||||
.async()
|
||||
.run()
|
||||
.andThenConsume(future -> {
|
||||
@@ -105,9 +116,27 @@ public class Pepper {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,10 +9,27 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
|
||||
public class PepperAnimationEvent extends AbstractPepperActionEvent {
|
||||
|
||||
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.onLabelReachedListener = onLabelReachedListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for PepperAnimationEvent
|
||||
*
|
||||
* @param animationName The name of the animation to play
|
||||
*/
|
||||
public PepperAnimationEvent(String animationName) {
|
||||
this(animationName, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -24,14 +41,25 @@ public class PepperAnimationEvent extends AbstractPepperActionEvent {
|
||||
* 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()))
|
||||
public Animate getAnimation(QiContext context) {
|
||||
Animation animation = AnimationBuilder.with(context)
|
||||
.withResources(context.getResources().getIdentifier(animationName, "raw", context.getPackageName()))
|
||||
.build();
|
||||
|
||||
return AnimateBuilder.with(this.context)
|
||||
Animate animate = AnimateBuilder.with(context)
|
||||
.withAnimation(animation)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
@@ -10,9 +10,7 @@ public class PepperSpeechEvent extends AbstractPepperActionEvent {
|
||||
public final String phrase;
|
||||
public final Locale locale;
|
||||
|
||||
public PepperSpeechEvent(QiContext context, String phrase, Locale locale)
|
||||
{
|
||||
super(context);
|
||||
public PepperSpeechEvent(String phrase, Locale locale) {
|
||||
this.locale = locale;
|
||||
this.phrase = phrase;
|
||||
}
|
||||
@@ -25,9 +23,8 @@ public class PepperSpeechEvent extends AbstractPepperActionEvent {
|
||||
/**
|
||||
* Returns a Say object, which can then be executed.
|
||||
*/
|
||||
public Say sayPhrase()
|
||||
{
|
||||
return SayBuilder.with(this.context)
|
||||
public Say getSay(QiContext context) {
|
||||
return SayBuilder.with(context)
|
||||
.withText(this.phrase)
|
||||
.withLocale(this.locale)
|
||||
.build();
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package com.example.fitbot.ui.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.VideoView;
|
||||
|
||||
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.ExerciseManager;
|
||||
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.FitnessCycle;
|
||||
import com.example.fitbot.util.processing.InputProcessor;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
@@ -22,14 +24,12 @@ import org.joml.Vector3f;
|
||||
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
|
||||
|
||||
// Private fields for the FitnessActivity class.
|
||||
private PersonalMotionPreviewElement personalMotionPreviewElement;
|
||||
private ExerciseStatusElement personalMotionPreviewElement;
|
||||
private InputProcessor motionProcessor;
|
||||
private Exercise currentExercise;
|
||||
|
||||
private QiContext qiContext;
|
||||
|
||||
// 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.",
|
||||
"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."
|
||||
@@ -39,7 +39,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
"Indien dit probleem zich voortzet, neem contact op met de ontwikkelaar.";
|
||||
|
||||
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;
|
||||
|
||||
@Override
|
||||
@@ -54,17 +54,16 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
|
||||
// Find the VideoView by its ID
|
||||
VideoView videoView = findViewById(R.id.videoView);
|
||||
FitnessCycle.playVideo(videoView, this);
|
||||
playVideo(videoView, this);
|
||||
NavigationManager.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
|
||||
|
||||
NavigationManager.hideSystemUI(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRobotFocusGained(QiContext qiContext) {
|
||||
this.qiContext = qiContext;
|
||||
// Find the VideoView by its ID
|
||||
// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext));
|
||||
|
||||
// Provide the context so that all queued actions can be performed.
|
||||
Pepper.provideContext(qiContext, this.getClass());
|
||||
|
||||
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
|
||||
|
||||
@@ -74,19 +73,18 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
||||
personalMotionPreviewElement.post(() -> {
|
||||
this.acquireExercise((exercise) -> {
|
||||
// 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);
|
||||
|
||||
personalMotionPreviewElement.provideQiContext(qiContext);
|
||||
personalMotionPreviewElement.initialize(exercise, motionProcessor, EXERCISE_COUNT);
|
||||
|
||||
motionProcessor.startListening();
|
||||
motionProcessor.setInputHandler(personalMotionPreviewElement);
|
||||
}, (n) -> {
|
||||
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_SEEK_HELP_MESSAGE, qiContext);
|
||||
int randomMessageIndex = (int) Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||
Pepper.speak(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex]);
|
||||
Pepper.speak(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE);
|
||||
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...
|
||||
(new Thread(() -> {
|
||||
Exercise exercise = ExerciseManager.retrieveExercise();
|
||||
if ( exercise == null)
|
||||
{
|
||||
if (exercise == null) {
|
||||
onFailedFetch.handle(null);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
onSuccessfulFetch.handle(exercise);
|
||||
}
|
||||
})).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
|
||||
public void onRobotFocusLost() {
|
||||
QiSDK.unregister(this, this);
|
||||
Pepper.provideContext(null, this.getClass()); // Remove the context (unavailable)
|
||||
|
||||
// Go to the main screen
|
||||
NavigationManager.navigateToActivity(this, MainActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRobotFocusRefused(String reason) {}
|
||||
public void onRobotFocusRefused(String reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if ( this.motionProcessor != null ) {
|
||||
if (this.motionProcessor != null) {
|
||||
this.motionProcessor.stopListening();
|
||||
this.motionProcessor = null;
|
||||
}
|
||||
QiSDK.unregister(this, this);
|
||||
Pepper.provideContext(null, this.getClass());
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
@@ -1,9 +1,7 @@
|
||||
package com.example.fitbot.ui.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.example.fitbot.R;
|
||||
import com.example.fitbot.util.NavigationManager;
|
||||
|
@@ -9,13 +9,12 @@ import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
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.FitnessActivity;
|
||||
import com.example.fitbot.ui.activities.MainActivity;
|
||||
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.InputProcessor;
|
||||
|
||||
@@ -26,12 +25,11 @@ import org.joml.Vector4f;
|
||||
|
||||
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.
|
||||
private InputProcessor motionProcessor;
|
||||
private Exercise exercise;
|
||||
private QiContext qiContext;
|
||||
private int exerciseCount;
|
||||
|
||||
private FitnessActivity parentActivity;
|
||||
@@ -41,10 +39,10 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
private final Paint backgroundPaint = new Paint();
|
||||
|
||||
// TODO: Remove
|
||||
private 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 viewMatrix = new Matrix4f(); // The view 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 ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
|
||||
|
||||
// TODO: Remove
|
||||
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);
|
||||
|
||||
if ( context instanceof Activity)
|
||||
{
|
||||
if (context instanceof Activity) {
|
||||
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
|
||||
* 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 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) {
|
||||
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
|
||||
@@ -96,8 +93,8 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
this.exercise = exercise;
|
||||
this.exerciseCount = exerciseCount;
|
||||
|
||||
// TODO: Remove
|
||||
this.axisVectors = new Vector2f[] {
|
||||
/* TODO: Remove */
|
||||
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(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())
|
||||
};
|
||||
|
||||
Pepper.speak(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]);
|
||||
|
||||
// Handler that is called every time the motion processor receives new data.
|
||||
this.motionProcessor.setInputHandler((rotationVector, deviceId) -> {
|
||||
|
||||
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
|
||||
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,
|
||||
// move back to the main screen.
|
||||
if ( this.parentActivity == null)
|
||||
{
|
||||
if (this.parentActivity == null) {
|
||||
// Move to main screen
|
||||
NavigationManager.navigateToActivity(getContext(), MainActivity.class);
|
||||
return;
|
||||
}
|
||||
// Move on to the next exercise, or finish.
|
||||
if ( this.exerciseCount > 0 )
|
||||
{
|
||||
if (this.exerciseCount > 0) {
|
||||
this.exerciseCount--;
|
||||
this.parentActivity.acquireExercise((newExercise) -> {
|
||||
this.motionProcessor.useExercise(newExercise);
|
||||
@@ -132,43 +128,27 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
// Move to main screen
|
||||
NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Finish the exercise.
|
||||
NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Adjust / remove
|
||||
/* TODO: Use raw vector */
|
||||
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());
|
||||
|
||||
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
|
||||
if (this.vectors.size() > 100)
|
||||
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.
|
||||
*
|
||||
@@ -203,7 +183,6 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
|
||||
@@ -211,17 +190,17 @@ public class PersonalMotionPreviewElement extends View implements IInputHandler
|
||||
/*if (this.exercise == null)
|
||||
return;*/
|
||||
|
||||
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length/2; i++)
|
||||
{
|
||||
startX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2].x));
|
||||
endX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2+1].x));
|
||||
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));
|
||||
|
||||
/* TODO: Remove */
|
||||
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length / 2; i++) {
|
||||
startX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2].x));
|
||||
endX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2 + 1].x));
|
||||
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);
|
||||
}
|
||||
|
||||
for ( Vector2f point : this.vectors)
|
||||
{
|
||||
for (Vector2f point : this.vectors) {
|
||||
canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint);
|
||||
}
|
||||
/*
|
@@ -11,8 +11,7 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
|
||||
|
||||
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());
|
||||
|
||||
Animation animation = AnimationBuilder.with(ctx)
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -6,23 +6,18 @@ import android.content.Intent;
|
||||
import android.view.View;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Sets up a button to navigate to a different activity when clicked.
|
||||
*
|
||||
* @param currentActivity The activity that contains the button
|
||||
* @param buttonId The ID of the button
|
||||
* @param targetActivity The activity to navigate to
|
||||
* @param buttonId The ID of the button
|
||||
* @param targetActivity The activity to navigate to
|
||||
*/
|
||||
public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class<? extends Activity> targetActivity) {
|
||||
Button button = currentActivity.findViewById(buttonId);
|
||||
if ( button == null )
|
||||
{
|
||||
if (button == null) {
|
||||
throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName());
|
||||
}
|
||||
button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity));
|
||||
@@ -32,7 +27,7 @@ public class NavigationManager {
|
||||
* Navigates to the target 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) {
|
||||
Intent intent = new Intent(currentActivity, targetActivity);
|
||||
@@ -45,7 +40,7 @@ public class NavigationManager {
|
||||
/**
|
||||
* Navigates to the target activity.
|
||||
*
|
||||
* @param context The context
|
||||
* @param context The context
|
||||
* @param targetActivity The target activity
|
||||
*/
|
||||
public static void navigateToActivity(Context context, Class<? extends Activity> targetActivity) {
|
||||
|
@@ -2,10 +2,6 @@ package com.example.fitbot.util.path;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class GesturePath {
|
||||
|
||||
// The vectors that make up the path.
|
||||
@@ -16,18 +12,18 @@ public class GesturePath {
|
||||
*
|
||||
* @param vectors The vectors that make up the path.
|
||||
*/
|
||||
public GesturePath(Vector3f[] vectors)
|
||||
{
|
||||
if ( vectors.length < 2)
|
||||
public GesturePath(Vector3f[] vectors) {
|
||||
if (vectors.length < 2)
|
||||
throw new IllegalArgumentException("A path must have at least two points.");
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a GesturePath with provided PathSegments.
|
||||
*
|
||||
* @param segments The PathSegments to initialize the path with.
|
||||
*/
|
||||
public GesturePath(PathSegment... segments) {
|
||||
@@ -46,11 +42,10 @@ public class GesturePath {
|
||||
/**
|
||||
* Method for retrieving the vectors of the GesturePath.
|
||||
*/
|
||||
public Vector3f[] getVectors()
|
||||
{
|
||||
public Vector3f[] getVectors() {
|
||||
Vector3f[] vectors = new Vector3f[segments.length + 1];
|
||||
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();
|
||||
|
||||
return vectors;
|
||||
@@ -64,11 +59,11 @@ public class GesturePath {
|
||||
*/
|
||||
public PathSegment closest(Vector3f reference) {
|
||||
// If there's only one segment, return that one.
|
||||
if ( segments.length == 1)
|
||||
if (segments.length == 1)
|
||||
return 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);
|
||||
|
||||
return closest;
|
||||
@@ -89,7 +84,7 @@ public class GesturePath {
|
||||
* Function for converting a string to a GesturePath object.
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first).
|
||||
*
|
||||
* @param input The string to convert
|
||||
|
@@ -12,7 +12,7 @@ public class PathSegment {
|
||||
* pointing straight upwards relative to the line, with a curvature of 0.0.
|
||||
*
|
||||
* @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) {
|
||||
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
|
||||
* 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) {
|
||||
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.y + t * (this.end.y - this.start.y)),
|
||||
(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.
|
||||
*/
|
||||
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.start.distance(reference);
|
||||
}
|
||||
@@ -92,8 +92,8 @@ public class PathSegment {
|
||||
/**
|
||||
* Function for returning the closest path segment to a reference point.
|
||||
*
|
||||
* @param first The first path segment to compare.
|
||||
* @param second The second path segment to compare.
|
||||
* @param first The first path segment to compare.
|
||||
* @param second The second path segment to compare.
|
||||
* @param referencePoint The reference point to compare to.
|
||||
* @return The closest path segment to the reference point.
|
||||
*/
|
||||
|
@@ -4,11 +4,12 @@ import org.joml.Vector3f;
|
||||
|
||||
public interface IInputHandler {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void accept(Vector3f rotationVector, int sensorId);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void accept(Vector3f rotationVector, int sensorId);
|
||||
|
||||
}
|
@@ -49,6 +49,7 @@ public class InputProcessor {
|
||||
* Function for setting the exercise to use.
|
||||
* This updates the user and target path and the
|
||||
* duration of the exercise.
|
||||
*
|
||||
* @param exercise The exercise to use the paths for.
|
||||
*/
|
||||
public void useExercise(Exercise exercise) {
|
||||
@@ -61,6 +62,7 @@ public class InputProcessor {
|
||||
|
||||
/**
|
||||
* Function for checking if the exercise has finished.
|
||||
*
|
||||
* @return True if the exercise has finished, false otherwise.
|
||||
*/
|
||||
public boolean hasFinished() {
|
||||
@@ -171,8 +173,7 @@ public class InputProcessor {
|
||||
*
|
||||
* @return The current progress of the exercise.
|
||||
*/
|
||||
public double getCurrentProgress()
|
||||
{
|
||||
public double getCurrentProgress() {
|
||||
return secondsPassed / exerciseDuration;
|
||||
}
|
||||
|
||||
@@ -208,8 +209,7 @@ public class InputProcessor {
|
||||
|
||||
// Ensure the indexes are within the bounds of the array
|
||||
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 0.0d;
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package com.example.fitbot.util.server;
|
||||
|
||||
import java.net.Socket;
|
||||
|
||||
/**
|
||||
* Interface for handling WebSocket events.
|
||||
*/
|
||||
|
@@ -10,16 +10,13 @@ import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
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.function.Consumer;
|
||||
|
||||
public class WebServer implements Runnable {
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
protected IWebServerHandler eventHandler = (input) -> {}; // No-op.
|
||||
protected IWebServerHandler eventHandler = (input) -> {
|
||||
}; // No-op.
|
||||
|
||||
private Thread thread;
|
||||
private final AtomicBoolean forceClose = new AtomicBoolean(false);
|
||||
@@ -84,7 +81,7 @@ public class WebServer implements Runnable {
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
|
@@ -56,7 +56,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.example.fitbot.ui.components.PersonalMotionPreviewElement
|
||||
<com.example.fitbot.ui.components.ExerciseStatusElement
|
||||
android:id="@+id/personalMotionPreviewElement"
|
||||
android:layout_width="350dp"
|
||||
android:layout_height="350dp" />
|
||||
|
Reference in New Issue
Block a user