Merge branch 'main' of https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66
This commit is contained in:
16
code/src/Fitbot/.idea/misc.xml
generated
16
code/src/Fitbot/.idea/misc.xml
generated
@@ -16,25 +16,33 @@
|
|||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml" value="0.165" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml" value="0.165" />
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_endscreen.xml" value="0.1" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_endscreen.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.1234375" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.1234375" />
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_help.xml" value="0.1" />
|
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.1" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.1234375" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.1234375" />
|
||||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.1234375" />
|
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.1234375" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.25" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.25" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/big_red_button_gradient.xml" value="0.2555" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/box_background.xml" value="0.2555" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml" value="0.346" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml" value="0.346" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/help2_background.xml" value="0.2395" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/help_background.xml" value="0.2395" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_home_48.xml" value="0.25" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_home_48.xml" value="0.25" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_settings_48.xml" value="0.25" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_settings_48.xml" value="0.25" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_star_rate_48.xml" value="0.25" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_star_rate_48.xml" value="0.25" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml" value="0.25" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml" value="0.25" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/rectangle.xml" value="0.2395" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml" value="0.346" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml" value="0.346" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_bicepvideo.xml" value="0.22826086956521738" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_bicepvideo.xml" value="0.22826086956521738" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.176" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml" value="0.4" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.6" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_help.xml" value="0.4" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.4" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main_screen.xml" value="0.1" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main_screen.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_power_screen.xml" value="0.1" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_power_screen.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_item.xml" value="0.1" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_item.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_menu.xml" value="0.1" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_menu.xml" value="0.1" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.125" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.264" />
|
||||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.125" />
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.264" />
|
||||||
|
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/menu/main_menu.xml" value="0.4" />
|
||||||
<entry key="app/src/main/res/layout/activity_end_screen.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/activity_end_screen.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/activity_fitness.xml" value="0.23550724637681159" />
|
<entry key="app/src/main/res/layout/activity_fitness.xml" value="0.23550724637681159" />
|
||||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||||
|
@@ -38,9 +38,6 @@ dependencies {
|
|||||||
implementation 'org.joml:joml:1.10.5'
|
implementation 'org.joml:joml:1.10.5'
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||||
implementation 'com.aldebaran:qisdk:1.7.5'
|
implementation 'com.aldebaran:qisdk:1.7.5'
|
||||||
|
@@ -3,21 +3,31 @@ package com.example.fitbot.exercise;
|
|||||||
public enum EMuscleGroup {
|
public enum EMuscleGroup {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
|
|
||||||
TORSO(0),
|
TORSO(0, new String[]{"upper body", "torso"}),
|
||||||
ARMS(1),
|
ARMS(1, new String[]{"arms", "arm", "shoulder"}),
|
||||||
LEGS(2),
|
LEGS(2, new String[]{"Lower body", "legs", "leg"});
|
||||||
BALANCE(3);
|
|
||||||
|
|
||||||
int muscleGroupIdentifier;
|
int muscleGroupIdentifier;
|
||||||
|
String[] muscleGroupNames;
|
||||||
|
|
||||||
EMuscleGroup(int identifier) {
|
EMuscleGroup(int identifier, String[] muscleGroupNames) {
|
||||||
this.muscleGroupIdentifier = identifier;
|
this.muscleGroupIdentifier = identifier;
|
||||||
|
this.muscleGroupNames = muscleGroupNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIdentifier() {
|
public int getIdentifier() {
|
||||||
return this.muscleGroupIdentifier;
|
return this.muscleGroupIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EMuscleGroup parse(String name)
|
||||||
|
{
|
||||||
|
for ( EMuscleGroup muscleGroup : EMuscleGroup.values())
|
||||||
|
for ( String muscleGroupName : muscleGroup.muscleGroupNames)
|
||||||
|
if ( muscleGroupName.equalsIgnoreCase(name))
|
||||||
|
return muscleGroup;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static EMuscleGroup parse(int identifier) {
|
public static EMuscleGroup parse(int identifier) {
|
||||||
for (EMuscleGroup muscleGroup : EMuscleGroup.values()) {
|
for (EMuscleGroup muscleGroup : EMuscleGroup.values()) {
|
||||||
if (muscleGroup.getIdentifier() == identifier) {
|
if (muscleGroup.getIdentifier() == identifier) {
|
||||||
|
@@ -3,26 +3,21 @@ package com.example.fitbot.exercise;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.example.fitbot.util.path.GesturePath;
|
import com.example.fitbot.util.path.GesturePath;
|
||||||
import com.example.fitbot.util.processing.IMotionDataConsumer;
|
|
||||||
import com.example.fitbot.util.server.IWebServerHandler;
|
import com.example.fitbot.util.server.IWebServerHandler;
|
||||||
import com.example.fitbot.util.server.WebServer;
|
import com.example.fitbot.util.server.WebServer;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class Exercise implements IWebServerHandler {
|
public class Exercise {
|
||||||
|
|
||||||
private EMuscleGroup muscleGroup;
|
|
||||||
private GesturePath leftPath;
|
|
||||||
private GesturePath rightPath;
|
|
||||||
private String title;
|
|
||||||
private String description;
|
|
||||||
private float segmentsPerSecond;
|
|
||||||
|
|
||||||
// Static fields.
|
|
||||||
private static WebServer webSocket;
|
|
||||||
private static Exercise currentExercise = null;
|
|
||||||
|
|
||||||
|
public final EMuscleGroup muscleGroup;
|
||||||
|
public final GesturePath leftPath;
|
||||||
|
public final GesturePath rightPath;
|
||||||
|
public final String title;
|
||||||
|
public final String description;
|
||||||
|
public final String imageUrl;
|
||||||
|
public final String videoUrl;
|
||||||
|
public final float exerciseTimeInSeconds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the AbstractExercise class.
|
* Constructor for the AbstractExercise class.
|
||||||
@@ -32,106 +27,17 @@ public class Exercise implements IWebServerHandler {
|
|||||||
* @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 segmentsPerSecond The number of segments per second.
|
* @param imageUrl The URL of the image.
|
||||||
* This determines how fast the exercise should be performed.
|
* @param videoUrl The URL of the video.
|
||||||
*/
|
*/
|
||||||
public Exercise(EMuscleGroup muscleGroup, String title, String description, GesturePath leftPath, GesturePath rightPath) {
|
public Exercise(EMuscleGroup muscleGroup, String title, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) {
|
||||||
this.muscleGroup = muscleGroup;
|
this.muscleGroup = muscleGroup;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.leftPath = leftPath;
|
this.leftPath = leftPath;
|
||||||
this.rightPath = rightPath;
|
this.rightPath = rightPath;
|
||||||
}
|
this.imageUrl = imageUrl;
|
||||||
|
this.videoUrl = videoUrl;
|
||||||
/**
|
this.exerciseTimeInSeconds = exerciseTimeInSeconds;
|
||||||
* Start the exercise.
|
|
||||||
* This method starts a WebSocket server
|
|
||||||
*/
|
|
||||||
public final void startExercise() {
|
|
||||||
|
|
||||||
// Ensure no other exercise is active.
|
|
||||||
if (currentExercise != null && currentExercise != this) {
|
|
||||||
currentExercise.__stopExercise();
|
|
||||||
Log.i("Exercises", "Another exercise was started when another was still running.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a WebSocket server is already running, change the event handler to be this class.
|
|
||||||
if (webSocket != null && webSocket.isConnected()) {
|
|
||||||
webSocket.setEventHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
webSocket = WebServer.createServer();
|
|
||||||
Objects.requireNonNull(webSocket, "WebSocket server could not be created.");
|
|
||||||
|
|
||||||
webSocket.setEventHandler(this);
|
|
||||||
currentExercise = this;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for ending this exercise and returning the grade of the performance
|
|
||||||
* of this activity.
|
|
||||||
*/
|
|
||||||
public final double finishExercise() {
|
|
||||||
this.__stopExercise();
|
|
||||||
|
|
||||||
// TODO: Implement grade calculation
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the exercise.
|
|
||||||
* This method stops the WebSocket server.
|
|
||||||
*/
|
|
||||||
private void __stopExercise() {
|
|
||||||
if (webSocket != null && webSocket.isConnected()) {
|
|
||||||
webSocket.stop();
|
|
||||||
webSocket = null;
|
|
||||||
}
|
|
||||||
currentExercise = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current exercise is the current activity.
|
|
||||||
*/
|
|
||||||
public final boolean isCurrentActivity() {
|
|
||||||
return currentExercise == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the muscle group of the exercise.
|
|
||||||
*/
|
|
||||||
public EMuscleGroup getMuscleGroup() {
|
|
||||||
return muscleGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path of the exercise.
|
|
||||||
*/
|
|
||||||
public GesturePath[] getPath() {
|
|
||||||
return new GesturePath[]{leftPath, rightPath};
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the speed of the exercise.
|
|
||||||
*/
|
|
||||||
public double getSegmentsPerSecond() {
|
|
||||||
return segmentsPerSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(String message) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,25 +4,35 @@ import com.example.fitbot.util.path.GesturePath;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
public class ExerciseManager {
|
public class ExerciseManager {
|
||||||
|
|
||||||
private static final String HOST_ADDRESS = "http://145.92.8.132";
|
private static final String HOST_ADDRESS = "http://145.92.8.132:443/";
|
||||||
|
|
||||||
|
// 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_MUSCLE_GROUP = "muscleGroup";
|
||||||
private static final String PROPERTY_DESC = "description";
|
private static final String PROPERTY_DESC = "description";
|
||||||
private static final String PROPERTY_VECTORS = "vector_data";
|
private static final String PROPERTY_IMAGE_URL = "imageUrl";
|
||||||
|
private static final String PROPERTY_VIDEO_URL = "videoUrl";
|
||||||
private static final String PROPERTY_NAME = "name";
|
private static final String PROPERTY_NAME = "name";
|
||||||
private static final String PROPERTY_MUSCLE_GROUP = "muscle_group";
|
private static final String PROPERTY_DATA = "data";
|
||||||
private static final String PROPERTY_SEGMENT_SPEED = "segment_speed";
|
private static final String PROPERTY_EXERCISE_DURATION = "exerciseDuration";
|
||||||
|
|
||||||
|
public static final int SENSOR_COUNT = 2;
|
||||||
|
|
||||||
|
private static final String[] REQUIRED_PROPERTIES = {
|
||||||
|
PROPERTY_MUSCLE_GROUP, PROPERTY_DESC, PROPERTY_IMAGE_URL,
|
||||||
|
PROPERTY_VIDEO_URL, PROPERTY_NAME, PROPERTY_DATA,
|
||||||
|
PROPERTY_EXERCISE_DURATION
|
||||||
|
};
|
||||||
|
|
||||||
private static final float DEFAULT_SEGMENT_SPEED = 1.0f;
|
private static final float DEFAULT_SEGMENT_SPEED = 1.0f;
|
||||||
|
|
||||||
@@ -36,13 +46,17 @@ public class ExerciseManager {
|
|||||||
*
|
*
|
||||||
* @return The response from the server.
|
* @return The response from the server.
|
||||||
*/
|
*/
|
||||||
private static String sendHTTP(String url, String method, String contentType, String body) {
|
public static String sendHTTP(String url, String method, String contentType, String body) {
|
||||||
try {
|
try {
|
||||||
URLConnection connection = new URL(url).openConnection();
|
URLConnection connection = new URL(url).openConnection();
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setDoInput(true);
|
||||||
connection.addRequestProperty("Content-Type", contentType);
|
connection.addRequestProperty("Content-Type", contentType);
|
||||||
connection.addRequestProperty("Request-Method", method);
|
connection.addRequestProperty("Request-Method", method);
|
||||||
connection.getOutputStream().write(body.getBytes());
|
|
||||||
connection.connect();
|
connection.connect();
|
||||||
|
// Send a body if it is present
|
||||||
|
if (body != null)
|
||||||
|
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();
|
||||||
@@ -60,23 +74,41 @@ public class ExerciseManager {
|
|||||||
/**
|
/**
|
||||||
* Function for retrieving an exercise from the Raspberry Pi Database.
|
* Function for retrieving an exercise from the Raspberry Pi Database.
|
||||||
*
|
*
|
||||||
* @param uniqueIdentifier The unique identifier of the exercise
|
|
||||||
* @return The exercise, if it exists on the server. Otherwise null.
|
* @return The exercise, if it exists on the server. Otherwise null.
|
||||||
*/
|
*/
|
||||||
public static Exercise retrieveExercise(String uniqueIdentifier) {
|
public static Exercise retrieveExercise() {
|
||||||
String response = sendHTTP(
|
String response = sendHTTP(
|
||||||
HOST_ADDRESS + "/acquire", "GET", "application/json", "{\"kind\":\"" + uniqueIdentifier + "\"}"
|
HOST_ADDRESS, "POST", "application/json", null
|
||||||
);
|
);
|
||||||
// Validate the response
|
// Validate the response
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
try {
|
try {
|
||||||
JsonObject content = JsonParser.parseString(response).getAsJsonObject();
|
JsonObject content = JsonParser.parseString(response).getAsJsonObject();
|
||||||
|
|
||||||
|
// Ensure all required properties are present
|
||||||
|
for (String property : REQUIRED_PROPERTIES) {
|
||||||
|
if (!content.has(property)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path data is split into two parts, due to the left and right hand.
|
||||||
|
// If one wants to add support for more sensors, one will have to adjust the Exercise
|
||||||
|
// class to support more paths.
|
||||||
|
String[] leftRightData = content.get(PROPERTY_DATA).getAsString().split(";");
|
||||||
|
|
||||||
|
if ( leftRightData.length != SENSOR_COUNT)
|
||||||
|
return null;
|
||||||
|
|
||||||
return new Exercise(
|
return new Exercise(
|
||||||
EMuscleGroup.parse(content.get(PROPERTY_MUSCLE_GROUP).getAsInt()),
|
EMuscleGroup.parse(content.get(PROPERTY_MUSCLE_GROUP).getAsString()),
|
||||||
content.get(PROPERTY_NAME).getAsString(),
|
content.get(PROPERTY_NAME).getAsString(),
|
||||||
content.get(PROPERTY_DESC).getAsString(),
|
content.get(PROPERTY_DESC).getAsString(),
|
||||||
gesturePathFromString(content.get(PROPERTY_VECTORS).getAsString()),
|
content.get(PROPERTY_IMAGE_URL).getAsString(),
|
||||||
gesturePathFromString(content.get(PROPERTY_SEGMENT_SPEED).getAsString())
|
content.get(PROPERTY_VIDEO_URL).getAsString(),
|
||||||
|
GesturePath.fromString(leftRightData[0]),
|
||||||
|
GesturePath.fromString(leftRightData[1]),
|
||||||
|
DEFAULT_SEGMENT_SPEED
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -84,42 +116,4 @@ public class ExerciseManager {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first).
|
|
||||||
*
|
|
||||||
* @param input The string to convert
|
|
||||||
* @return The GesturePath object
|
|
||||||
*/
|
|
||||||
private static GesturePath gesturePathFromString(String input) {
|
|
||||||
byte[] bytes = input.getBytes();
|
|
||||||
|
|
||||||
// Check if the input string contains a valid amount of bytes (12 bytes per vector)
|
|
||||||
if (input.length() % 12 != 0) {
|
|
||||||
throw new IllegalArgumentException("Invalid input string length");
|
|
||||||
}
|
|
||||||
GesturePath.Builder builder = new GesturePath.Builder();
|
|
||||||
|
|
||||||
float[] xyz = new float[3];
|
|
||||||
for (int i = 0; i < bytes.length; i += 12) {
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
|
|
||||||
xyz[j] = Float.intBitsToFloat(
|
|
||||||
(bytes[i + j * 4] & 0xFF) << 24 |
|
|
||||||
(bytes[i + j * 4 + 1] & 0xFF) << 16 |
|
|
||||||
(bytes[i + j * 4 + 2] & 0xFF) << 8 |
|
|
||||||
(bytes[i + j * 4 + 3] & 0xFF)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
builder.addVector(new Vector3f(
|
|
||||||
xyz[0], xyz[1], xyz[2]
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ public class EndScreenActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_end_screen);
|
setContentView(R.layout.activity_end_screen);
|
||||||
|
|
||||||
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.homeButton, MainActivity.class);
|
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonEndScreen, MainActivity.class);
|
||||||
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.continueButton, FitnessActivity.class);
|
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.startButtonEndScreen, FitnessActivity.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@ package com.example.fitbot.ui.activities;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.VideoView;
|
import android.widget.VideoView;
|
||||||
|
|
||||||
import com.aldebaran.qi.sdk.QiContext;
|
import com.aldebaran.qi.sdk.QiContext;
|
||||||
@@ -10,25 +11,43 @@ import com.aldebaran.qi.sdk.RobotLifecycleCallbacks;
|
|||||||
import com.aldebaran.qi.sdk.design.activity.RobotActivity;
|
import com.aldebaran.qi.sdk.design.activity.RobotActivity;
|
||||||
import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayStrategy;
|
import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayStrategy;
|
||||||
import com.example.fitbot.R;
|
import com.example.fitbot.R;
|
||||||
import com.example.fitbot.exercise.EMuscleGroup;
|
|
||||||
import com.example.fitbot.exercise.Exercise;
|
import com.example.fitbot.exercise.Exercise;
|
||||||
|
import com.example.fitbot.exercise.ExerciseManager;
|
||||||
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
||||||
import com.example.fitbot.util.ButtonNavigation;
|
import com.example.fitbot.util.ButtonNavigation;
|
||||||
import com.example.fitbot.util.FitnessCycle;
|
import com.example.fitbot.util.FitnessCycle;
|
||||||
import com.example.fitbot.util.path.GesturePath;
|
import com.example.fitbot.util.processing.InputProcessor;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
|
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
|
||||||
|
|
||||||
PersonalMotionPreviewElement personalMotionPreviewElement;
|
// Private fields for the FitnessActivity class.
|
||||||
|
private PersonalMotionPreviewElement 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[] {
|
||||||
|
"Ik heb momenteel helaas wat moeite met het ophalen van oefeningen, sorry.",
|
||||||
|
"Het lijkt erop dat de oefeningen op een misterieus avontuur zijn. Even wachten tot ze terug zijn.",
|
||||||
|
"Ssst, de oefeningen slapen nog, probeer het later nog eens."
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE =
|
||||||
|
"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 float EXERCISE_SPEED_MULTIPLIER = 1.0f;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
QiSDK.register(this, this);
|
QiSDK.register(this, this);
|
||||||
|
|
||||||
setContentView(R.layout.activity_fitness);
|
setContentView(R.layout.activity_fitness);
|
||||||
|
|
||||||
// Remove the ugly ass bar on top of the view
|
// Remove the ugly ass bar on top of the view
|
||||||
@@ -36,50 +55,62 @@ 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);
|
FitnessCycle.playVideo(videoView, this);
|
||||||
|
ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
|
||||||
ButtonNavigation.setupButtonNavigation(this, R.id.homeButton, MainActivity.class);
|
|
||||||
ButtonNavigation.setupButtonNavigation(this, R.id.buttonComplete, EndScreenActivity.class);
|
|
||||||
// Implement your logic when the robot focus is gained
|
// Implement your logic when the robot focus is gained
|
||||||
|
|
||||||
GesturePath.Builder gesturePathBuilder = new GesturePath.Builder();
|
|
||||||
|
|
||||||
/* Generate a random path to test the tracking system */
|
|
||||||
for ( int i = 0; i < 40; i++)
|
|
||||||
{
|
|
||||||
gesturePathBuilder.addVector(
|
|
||||||
new Vector3f(
|
|
||||||
(float)Math.cos(Math.PI + (Math.PI / 40.0f) * i),
|
|
||||||
(float)Math.sin(Math.PI + (Math.PI / 40.0f) * i),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
|
|
||||||
personalMotionPreviewElement.post(() -> {
|
|
||||||
Log.i("FitnessActivity", "PersonalMotionPreviewElement.post()");
|
|
||||||
|
|
||||||
Exercise exercise = new Exercise(EMuscleGroup.ARMS, "Bicep Curls", "Oefening voor de biceps.", gesturePathBuilder.build(), gesturePathBuilder.build());
|
|
||||||
|
|
||||||
personalMotionPreviewElement.initialize(exercise);
|
|
||||||
personalMotionPreviewElement.provideQiContext(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRobotFocusGained(QiContext qiContext) {
|
public void onRobotFocusGained(QiContext qiContext) {
|
||||||
|
this.qiContext = qiContext;
|
||||||
// Find the VideoView by its ID
|
// Find the VideoView by its ID
|
||||||
// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext));
|
// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext));
|
||||||
Log.i("Motion", "qiContext provided");
|
|
||||||
|
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
|
||||||
|
|
||||||
|
// Initialize the element whenever it has been added to the screen.
|
||||||
|
// This will provide the element with the appropriate dimensions for drawing
|
||||||
|
// the canvas properly.
|
||||||
|
personalMotionPreviewElement.post(() -> {
|
||||||
|
Exercise exercise = this.acquireExercise();
|
||||||
|
if ( exercise == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Acquire paths from the exercise and provide them to the motion processor
|
||||||
|
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);
|
||||||
|
});
|
||||||
personalMotionPreviewElement.provideQiContext(qiContext);
|
personalMotionPreviewElement.provideQiContext(qiContext);
|
||||||
|
|
||||||
// FitnessCycle.playVideo(qiContext, videoView, this);
|
// FitnessCycle.playVideo(qiContext, videoView, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquire an exercise from the ExerciseManager.
|
||||||
|
* Whenever the retrieval failed, it will have the robot say something to the user
|
||||||
|
* to inform them about the issue.
|
||||||
|
*
|
||||||
|
* @return The acquired exercise, or null if the exercise could not be retrieved.
|
||||||
|
*/
|
||||||
|
public Exercise acquireExercise() {
|
||||||
|
Exercise exercise = ExerciseManager.retrieveExercise();
|
||||||
|
if ( exercise == null && this.qiContext != null)
|
||||||
|
{
|
||||||
|
int randomMessageIndex = (int)Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||||
|
FitnessCycle.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
||||||
|
FitnessCycle.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, qiContext);
|
||||||
|
}
|
||||||
|
return exercise;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRobotFocusLost() {
|
public void onRobotFocusLost() {
|
||||||
// Implement your logic when the robot focus is lost
|
// Implement your logic when the robot focus is lost
|
||||||
@@ -94,6 +125,8 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
QiSDK.unregister(this, this);
|
QiSDK.unregister(this, this);
|
||||||
this.personalMotionPreviewElement.onDestroy();
|
this.motionProcessor.stopListening();
|
||||||
|
this.motionProcessor = null;
|
||||||
|
this.personalMotionPreviewElement.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@ package com.example.fitbot.ui.activities;
|
|||||||
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import com.example.fitbot.R;
|
import com.example.fitbot.R;
|
||||||
import com.example.fitbot.util.ButtonNavigation;
|
import com.example.fitbot.util.ButtonNavigation;
|
||||||
@@ -13,7 +14,18 @@ public class HelpActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_help);
|
setContentView(R.layout.activity_help);
|
||||||
|
|
||||||
ButtonNavigation.setupButtonNavigation(this, R.id.homeButton, MainActivity.class);
|
ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonHelp, MainActivity.class);
|
||||||
|
|
||||||
|
// Hide system UI
|
||||||
|
hideSystemUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideSystemUI() {
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
// Hide the status bar and navigation bar
|
||||||
|
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||||
|
decorView.setSystemUiVisibility(uiOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,6 +11,8 @@ import android.support.v7.app.ActionBarDrawerToggle;
|
|||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import com.example.fitbot.R;
|
import com.example.fitbot.R;
|
||||||
@@ -18,20 +20,24 @@ import com.example.fitbot.util.ButtonNavigation;
|
|||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
//Variables
|
// Variables
|
||||||
DrawerLayout drawerLayout;
|
DrawerLayout drawerLayout;
|
||||||
NavigationView navigationView;
|
NavigationView navigationView;
|
||||||
Toolbar toolbar;
|
Toolbar toolbar;
|
||||||
Button startButton;
|
Button startButton;
|
||||||
|
|
||||||
@SuppressLint("WrongViewCast")
|
@SuppressLint("WrongViewCast")
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
Button startButton = findViewById(R.id.startButton);
|
// Set full screen mode to hide status bar
|
||||||
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
|
||||||
|
|
||||||
|
startButton = findViewById(R.id.startButtonMain);
|
||||||
startButton.setOnClickListener(v -> {
|
startButton.setOnClickListener(v -> {
|
||||||
Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bicepvideo);
|
Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bicepvideo);
|
||||||
Intent intent = new Intent(MainActivity.this, FitnessActivity.class);
|
Intent intent = new Intent(MainActivity.this, FitnessActivity.class);
|
||||||
@@ -39,7 +45,11 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
|
|
||||||
setUpUi(); // Set up the UI
|
// Set up the UI
|
||||||
|
setUpUi();
|
||||||
|
|
||||||
|
// Hide system UI
|
||||||
|
hideSystemUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpUi() {
|
private void setUpUi() {
|
||||||
@@ -47,31 +57,65 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
drawerLayout = findViewById(R.id.drawer_layout);
|
drawerLayout = findViewById(R.id.drawer_layout);
|
||||||
navigationView = findViewById(R.id.nav_view);
|
navigationView = findViewById(R.id.nav_view);
|
||||||
toolbar = findViewById(R.id.toolbar);
|
toolbar = findViewById(R.id.toolbar);
|
||||||
startButton = findViewById(R.id.startButton);
|
startButton = findViewById(R.id.startButtonMain);
|
||||||
|
|
||||||
ButtonNavigation.setupButtonNavigation(this, R.id.startButton, FitnessActivity.class);
|
// Hide the action bar
|
||||||
ButtonNavigation.setupButtonNavigation(this, R.id.helpButton, HelpActivity.class);
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().hide();
|
||||||
|
}
|
||||||
|
ButtonNavigation.setupButtonNavigation(this, R.id.startButtonMain, FitnessActivity.class);
|
||||||
|
ButtonNavigation.setupButtonNavigation(this, R.id.helpButtonMain, HelpActivity.class);
|
||||||
|
|
||||||
/*---Tool Bar---*/
|
/*---Tool Bar---*/
|
||||||
setSupportActionBar(toolbar); // Make the toolbar act as the action bar
|
setSupportActionBar(toolbar); // Make the toolbar act as the action bar
|
||||||
getSupportActionBar().setDisplayShowTitleEnabled(false); // Remove the title from the toolbar
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayShowTitleEnabled(false); // Remove the title from the toolbar
|
||||||
|
}
|
||||||
|
|
||||||
/*---Navigation Drawer Menu---*/
|
/*---Navigation Drawer Menu---*/
|
||||||
navigationView.bringToFront(); // Make the navigation drawer menu clickable
|
navigationView.bringToFront(); // Make the navigation drawer menu clickable
|
||||||
|
|
||||||
ActionBarDrawerToggle toggle=new // Create a toggle for the navigation drawer
|
ActionBarDrawerToggle toggle = new // Create a toggle for the navigation drawer
|
||||||
ActionBarDrawerToggle(this,drawerLayout,toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
|
ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
|
||||||
|
@Override
|
||||||
|
public void onDrawerOpened(View drawerView) {
|
||||||
|
super.onDrawerOpened(drawerView);
|
||||||
|
hideSystemUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawerClosed(View drawerView) {
|
||||||
|
super.onDrawerClosed(drawerView);
|
||||||
|
hideSystemUI();
|
||||||
|
}
|
||||||
|
};
|
||||||
drawerLayout.addDrawerListener(toggle);
|
drawerLayout.addDrawerListener(toggle);
|
||||||
toggle.syncState(); // Synchronize the state of the navigation drawer
|
toggle.syncState(); // Synchronize the state of the navigation drawer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hideSystemUI() {
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
// Hide the status bar and navigation bar
|
||||||
|
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||||
|
decorView.setSystemUiVisibility(uiOptions);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed(){ // Close the navigation drawer when the back button is pressed
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
if(drawerLayout.isDrawerOpen(GravityCompat.START)){
|
super.onWindowFocusChanged(hasFocus);
|
||||||
drawerLayout.closeDrawer(GravityCompat.START);
|
if (hasFocus) {
|
||||||
|
hideSystemUI();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{super.onBackPressed();
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() { // Close the navigation drawer when the back button is pressed
|
||||||
|
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||||
|
drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,18 +1,23 @@
|
|||||||
package com.example.fitbot.ui.components;
|
package com.example.fitbot.ui.components;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.AttributeSet;
|
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.aldebaran.qi.sdk.QiContext;
|
||||||
import com.example.fitbot.exercise.Exercise;
|
import com.example.fitbot.exercise.Exercise;
|
||||||
|
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.FitnessCycle;
|
import com.example.fitbot.util.FitnessCycle;
|
||||||
import com.example.fitbot.util.path.GesturePath;
|
import com.example.fitbot.util.processing.IInputHandler;
|
||||||
import com.example.fitbot.util.processing.MotionProcessor;
|
import com.example.fitbot.util.processing.InputProcessor;
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector2f;
|
import org.joml.Vector2f;
|
||||||
@@ -20,61 +25,58 @@ import org.joml.Vector3f;
|
|||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public class PersonalMotionPreviewElement extends View {
|
public class PersonalMotionPreviewElement extends View implements IInputHandler {
|
||||||
|
|
||||||
private GesturePath[] paths;
|
|
||||||
private MotionProcessor motionProcessor;
|
|
||||||
|
|
||||||
private double pathTime = 0.0D; // The timestamp at which the path is currently at.
|
|
||||||
private final AtomicInteger exerciseProgress = new AtomicInteger(0); // The progress of the exercise. Ranges from 0 to 1000.
|
|
||||||
|
|
||||||
private QiContext qiContext;
|
|
||||||
|
|
||||||
|
// Fields regarding Exercise and speech handling.
|
||||||
|
private InputProcessor motionProcessor;
|
||||||
private Exercise exercise;
|
private Exercise exercise;
|
||||||
|
private QiContext qiContext;
|
||||||
|
private int exerciseCount;
|
||||||
|
|
||||||
private Path targetPath; // The path the user is supposed to follow.
|
private FitnessActivity parentActivity;
|
||||||
private Path actualPath; // The path the user is currently following.
|
|
||||||
|
|
||||||
private final Paint referencePaint = new Paint();
|
private final Paint userProgressPaint = new Paint();
|
||||||
private final Paint targetPaint = new Paint();
|
private final Paint borderPaint = new Paint();
|
||||||
private final Paint backgroundColor = new Paint();
|
private final Paint backgroundPaint = new Paint();
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
private Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation.
|
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 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 ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
private Vector2f[] axisVectors = new Vector2f[0];
|
private Vector2f[] axisVectors = new Vector2f[0];
|
||||||
|
|
||||||
|
|
||||||
|
private static final String[] STARTING_PHRASES = {
|
||||||
private static final String[] USER_PHRASES = {
|
|
||||||
"Veel success met de oefening!",
|
"Veel success met de oefening!",
|
||||||
"Je kan het!",
|
"Je kan het!",
|
||||||
"Veel plezier!"
|
"Veel plezier!"
|
||||||
};
|
};
|
||||||
|
|
||||||
private double timePassed = 0.0D; // The time that has passed since the start of the exercise, in seconds.
|
|
||||||
private long startingTime = 0L;
|
|
||||||
|
|
||||||
private Vector2f screenDimensions = new Vector2f(); // Width and height dimensions of the screen
|
|
||||||
|
|
||||||
public PersonalMotionPreviewElement(Context context, AttributeSet attrs) {
|
public PersonalMotionPreviewElement(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|
||||||
this.referencePaint.setColor(0xFFFF0000); // Red
|
if ( context instanceof Activity)
|
||||||
this.referencePaint.setStyle(Paint.Style.FILL);
|
{
|
||||||
this.referencePaint.setStrokeWidth(5.0f);
|
this.parentActivity = (FitnessActivity) context;
|
||||||
this.referencePaint.setAntiAlias(true);
|
}
|
||||||
|
|
||||||
|
this.userProgressPaint.setColor(0xFFFF0000); // Red
|
||||||
|
this.userProgressPaint.setStyle(Paint.Style.FILL);
|
||||||
|
this.userProgressPaint.setStrokeWidth(5.0f);
|
||||||
|
this.userProgressPaint.setAntiAlias(true);
|
||||||
|
|
||||||
// Target paint is the filling of the target path.
|
// Target paint is the filling of the target path.
|
||||||
this.targetPaint.setColor(-1);
|
this.borderPaint.setColor(-1);
|
||||||
this.targetPaint.setStyle(Paint.Style.STROKE);
|
this.borderPaint.setStyle(Paint.Style.STROKE);
|
||||||
this.targetPaint.setStrokeWidth(5.0f);
|
this.borderPaint.setStrokeWidth(5.0f);
|
||||||
this.targetPaint.setAntiAlias(true);
|
this.borderPaint.setAntiAlias(true);
|
||||||
|
|
||||||
|
this.backgroundPaint.setColor(0xFF000000); // Black
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,34 +86,28 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
* 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 exerciseCount The total amount of exercises that the user has to perform.
|
||||||
*/
|
*/
|
||||||
public void initialize(Exercise exercise) {
|
public void initialize(@Nullable Exercise exercise, InputProcessor motionProcessor, int exerciseCount) {
|
||||||
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
|
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
|
||||||
this.backgroundColor.setColor(0xFF000000); // Black
|
|
||||||
|
|
||||||
this.screenDimensions.x = this.getWidth();
|
|
||||||
this.screenDimensions.y = this.getHeight();
|
|
||||||
this.actualPath = new Path();
|
|
||||||
this.targetPath = new Path();
|
|
||||||
|
|
||||||
this.startingTime = System.nanoTime(); // Set the last time to the current time
|
|
||||||
|
|
||||||
|
this.motionProcessor = motionProcessor;
|
||||||
this.exercise = exercise;
|
this.exercise = exercise;
|
||||||
this.paths = exercise.getPath();
|
this.exerciseCount = exerciseCount;
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
this.axisVectors = new Vector2f[] {
|
this.axisVectors = new Vector2f[] {
|
||||||
|
projectVertex(new Vector3f(-5.0f, 0, 0), getWidth(), getHeight()),
|
||||||
projectVertex(new Vector3f(-100.0f, 0, 0), getWidth(), getHeight()),
|
projectVertex(new Vector3f(5.0f, 0, 0), getWidth(), getHeight()),
|
||||||
projectVertex(new Vector3f(100.0f, 0, 0), getWidth(), getHeight()),
|
projectVertex(new Vector3f(0, -5.0f, 0), getWidth(), getHeight()),
|
||||||
projectVertex(new Vector3f(0, -100.0f, 0), getWidth(), getHeight()),
|
projectVertex(new Vector3f(0, 5.0f, 0), getWidth(), getHeight()),
|
||||||
projectVertex(new Vector3f(0, 100.0f, 0), getWidth(), getHeight()),
|
projectVertex(new Vector3f(0, 0, -5.0f), getWidth(), getHeight()),
|
||||||
projectVertex(new Vector3f(0, 0, -100.0f), getWidth(), getHeight()),
|
projectVertex(new Vector3f(0, 0, 5.0f), getWidth(), getHeight())
|
||||||
projectVertex(new Vector3f(0, 0, 100.0f), getWidth(), getHeight())
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
if ( this.motionProcessor != null )
|
if ( this.motionProcessor != null )
|
||||||
this.motionProcessor.stopListening();
|
this.motionProcessor.stopListening();
|
||||||
@@ -128,35 +124,55 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
*/
|
*/
|
||||||
public void provideQiContext(QiContext context) {
|
public void provideQiContext(QiContext context) {
|
||||||
this.qiContext = context;
|
this.qiContext = context;
|
||||||
if ( this.motionProcessor != null )
|
|
||||||
this.motionProcessor.stopListening();
|
|
||||||
|
|
||||||
this.motionProcessor = new MotionProcessor();
|
|
||||||
this.motionProcessor.startListening();
|
|
||||||
|
|
||||||
// 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.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate, deviceId) -> {
|
this.motionProcessor.setInputHandler((rotationVector, deviceId) -> {
|
||||||
int progress = (int)this.motionProcessor.getError(this.paths[0], processed);
|
|
||||||
this.exerciseProgress.set(Math.min(1000, Math.max(0, progress)));
|
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
|
||||||
Log.i("MotionProcessor", "Processed data: " + progress + " (" + preprocessed + ")");
|
Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed()));
|
||||||
Vector2f parsed = projectVertex(processed, this.getWidth(), this.getHeight());
|
|
||||||
|
if ( this.motionProcessor.hasFinished())
|
||||||
|
{
|
||||||
|
if ( this.parentActivity == null)
|
||||||
|
{
|
||||||
|
// Move to main screen
|
||||||
|
this.destroy();
|
||||||
|
Intent intent = new Intent(getContext(), MainActivity.class);
|
||||||
|
getContext().startActivity(intent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Move on to the next exercise, or finish.
|
||||||
|
if ( this.exerciseCount > 0 )
|
||||||
|
{
|
||||||
|
this.exerciseCount--;
|
||||||
|
this.exercise = this.parentActivity.acquireExercise();
|
||||||
|
this.motionProcessor.useExercise(this.exercise);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Finish the exercise.
|
||||||
|
this.destroy();
|
||||||
|
Intent intent = new Intent(getContext(), EndScreenActivity.class);
|
||||||
|
getContext().startActivity(intent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Adjust / remove
|
||||||
|
vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight()));
|
||||||
|
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
|
||||||
|
Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight());
|
||||||
|
|
||||||
this.vectors.add(parsed);
|
this.vectors.add(parsed);
|
||||||
// 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();
|
||||||
});
|
});
|
||||||
saySomethingNice();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to say something nice to the user :)
|
|
||||||
*/
|
|
||||||
private void saySomethingNice()
|
|
||||||
{
|
|
||||||
if (this.qiContext == null)
|
if (this.qiContext == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FitnessCycle.say(USER_PHRASES[(int) Math.floor(Math.random() * USER_PHRASES.length)], this.qiContext);
|
FitnessCycle.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)], this.qiContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,6 +181,7 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
* @param exercise The exercise that the user is currently performing.
|
* @param exercise The exercise that the user is currently performing.
|
||||||
*/
|
*/
|
||||||
public void setExercise(Exercise exercise) {
|
public void setExercise(Exercise exercise) {
|
||||||
|
this.motionProcessor.useExercise(exercise);
|
||||||
this.exercise = exercise;
|
this.exercise = exercise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,12 +195,12 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
// Perspective transformation conserves the depth of the object
|
// Perspective transformation conserves the depth of the object
|
||||||
projectionMatrix
|
projectionMatrix
|
||||||
.identity()
|
.identity()
|
||||||
.perspective((float) Math.toRadians(70), (float) virtualWidth / virtualHeight, .01f, 10000.0f);
|
.perspective((float) Math.toRadians(70), (float) virtualWidth / virtualHeight, .01f, 1000.0f);
|
||||||
|
|
||||||
// Convert world coordinates to screen-space using MVP matrix
|
// Convert world coordinates to screen-space using MVP matrix
|
||||||
Vector4f screenCoordinates = new Vector4f(point, 1.0f)
|
Vector4f screenCoordinates = new Vector4f(point, 1.0f)
|
||||||
.mul(this.projectionMatrix)
|
.mul(this.viewMatrix)
|
||||||
.mul(this.viewMatrix);
|
.mul(this.projectionMatrix);
|
||||||
|
|
||||||
// Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight)
|
// Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight)
|
||||||
float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth;
|
float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth;
|
||||||
@@ -195,23 +212,23 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDraw(Canvas canvas) {
|
public void onDraw(Canvas canvas) {
|
||||||
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor);
|
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
|
||||||
this.setBackgroundColor(0xFF000000); // Black
|
this.setBackgroundColor(0xFF000000); // Black
|
||||||
/*if (this.exercise == null)
|
/*if (this.exercise == null)
|
||||||
return;*/
|
return;*/
|
||||||
|
|
||||||
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length/2; i++)
|
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length/2; i++)
|
||||||
{
|
{
|
||||||
startX = (int)Math.max(0, Math.min(this.screenDimensions.x, (int)axisVectors[i*2].x));
|
startX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2].x));
|
||||||
endX = (int)Math.max(0, Math.min(this.screenDimensions.x, (int)axisVectors[i*2+1].x));
|
endX = (int)Math.max(0, Math.min(this.getWidth(), (int)axisVectors[i*2+1].x));
|
||||||
startY = (int)Math.max(0, Math.min(this.screenDimensions.y, (int)axisVectors[i*2].y));
|
startY = (int)Math.max(0, Math.min(this.getHeight(), (int)axisVectors[i*2].y));
|
||||||
endY = (int)Math.max(0, Math.min(this.screenDimensions.y, (int)axisVectors[i*2+1].y));
|
endY = (int)Math.max(0, Math.min(this.getHeight(), (int)axisVectors[i*2+1].y));
|
||||||
canvas.drawLine(startX, startY, endX, endY, this.targetPaint);
|
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.referencePaint);
|
canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// Draw target circle
|
// Draw target circle
|
||||||
@@ -228,7 +245,10 @@ public class PersonalMotionPreviewElement extends View {
|
|||||||
);*/
|
);*/
|
||||||
|
|
||||||
this.invalidate();
|
this.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Vector3f rotationVector, int sensorId) {
|
||||||
|
|
||||||
timePassed = (System.nanoTime() - startingTime) / 1E9D;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,19 @@ public class GesturePath {
|
|||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for retrieving the vectors of the GesturePath.
|
||||||
|
*/
|
||||||
|
public Vector3f[] getVectors()
|
||||||
|
{
|
||||||
|
Vector3f[] vectors = new Vector3f[segments.length + 1];
|
||||||
|
vectors[0] = segments[0].getStart();
|
||||||
|
for ( int i = 0; i < segments.length; i++)
|
||||||
|
vectors[i + 1] = segments[i].getEnd();
|
||||||
|
|
||||||
|
return vectors;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for retrieving the closest path segment to a reference point.
|
* Method for retrieving the closest path segment to a reference point.
|
||||||
*
|
*
|
||||||
@@ -71,48 +84,40 @@ public class GesturePath {
|
|||||||
return closest(referencePoint).difference(referencePoint); // Get the closest segment and calculate the error.
|
return closest(referencePoint).difference(referencePoint); // Get the closest segment and calculate the error.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder class for the GesturePath object.
|
|
||||||
public static class Builder {
|
|
||||||
|
|
||||||
// List of vectors to add to the GesturePath object.
|
/**
|
||||||
private final List<Vector3f> vectors;
|
* 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.
|
||||||
|
*
|
||||||
|
* Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first).
|
||||||
|
*
|
||||||
|
* @param input The string to convert
|
||||||
|
* @return The GesturePath object
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
public static GesturePath fromString(String input) {
|
||||||
* Constructor for the Builder object.
|
byte[] bytes = input.getBytes();
|
||||||
*
|
|
||||||
* @param vectors The list of vectors to add.
|
// Check if the input string contains a valid amount of bytes (12 bytes per vector)
|
||||||
*/
|
if (input.length() % 12 != 0) {
|
||||||
public Builder(List<Vector3f> vectors) {
|
throw new IllegalArgumentException("Invalid input string length");
|
||||||
this.vectors = vectors;
|
|
||||||
}
|
}
|
||||||
|
Vector3f[] vectors = new Vector3f[input.length() / 12];
|
||||||
|
|
||||||
/**
|
float[] xyz = new float[3];
|
||||||
* Default constructor for the Builder object.
|
for (int i = 0; i < bytes.length; i += 12) {
|
||||||
*/
|
for (int j = 0; j < 3; j++) {
|
||||||
public Builder() {
|
|
||||||
this.vectors = new ArrayList<>();
|
xyz[j] = Float.intBitsToFloat(
|
||||||
|
(bytes[i + j * 4] & 0xFF) << 24 |
|
||||||
|
(bytes[i + j * 4 + 1] & 0xFF) << 16 |
|
||||||
|
(bytes[i + j * 4 + 2] & 0xFF) << 8 |
|
||||||
|
(bytes[i + j * 4 + 3] & 0xFF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
vectors[i / 12] = new Vector3f(xyz[0], xyz[1], xyz[2]);
|
||||||
}
|
}
|
||||||
|
return new GesturePath(vectors);
|
||||||
/**
|
|
||||||
* Adds a vector to the GesturePath object.
|
|
||||||
*
|
|
||||||
* @param vector The vector to add.
|
|
||||||
* @return The Builder object.
|
|
||||||
*/
|
|
||||||
public Builder addVector(Vector3f vector) {
|
|
||||||
vectors.add(vector);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the GesturePath object.
|
|
||||||
*
|
|
||||||
* @return The GesturePath object.
|
|
||||||
*/
|
|
||||||
public GesturePath build() {
|
|
||||||
return new GesturePath(vectors.toArray(new Vector3f[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
@@ -1,16 +0,0 @@
|
|||||||
package com.example.fitbot.util.processing;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
public interface IMotionDataConsumer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for accepting motion data and the transformed vector.
|
|
||||||
* @param transformedVector The transformed vector.
|
|
||||||
* @param motionData The input motion data.
|
|
||||||
* @param sampleIndex The index of the current sample
|
|
||||||
* @param sampleRate The sample rate.
|
|
||||||
*/
|
|
||||||
void accept(Vector3f transformedVector, MotionData motionData, int sampleIndex, double sampleRate, int sensorId);
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,236 @@
|
|||||||
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.example.fitbot.exercise.Exercise;
|
||||||
|
import com.example.fitbot.util.server.WebServer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class InputProcessor {
|
||||||
|
|
||||||
|
private Vector3f[][] selfRotationVectorPaths; // Relative path of the motion data
|
||||||
|
private Vector3f[][] targetRotationVectorPaths; // Target path of the motion data
|
||||||
|
|
||||||
|
private final float sampleRate; // The sample rate of the motion sensor
|
||||||
|
private float exerciseDuration;
|
||||||
|
|
||||||
|
// How many seconds have passed since the start of the exercise.
|
||||||
|
// The exercise starts whenever the `startListening` function is called.
|
||||||
|
private double secondsPassed = 0.0D;
|
||||||
|
private long lastTime;
|
||||||
|
|
||||||
|
private IInputHandler motionDataConsumer = (rotationVector, deviceId) -> {
|
||||||
|
};
|
||||||
|
private WebServer server;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for the motion processor.
|
||||||
|
*
|
||||||
|
* @param paths The target paths of the motion data.
|
||||||
|
* The length of this array must be equal to the
|
||||||
|
* amount of sensors available.
|
||||||
|
* @param inputSampleRate The sample rate of the motion sensor.
|
||||||
|
*/
|
||||||
|
public InputProcessor(Vector3f[][] paths, float exerciseTime, float inputSampleRate) {
|
||||||
|
selfRotationVectorPaths = new Vector3f[paths.length][(int) (exerciseTime * inputSampleRate)];
|
||||||
|
targetRotationVectorPaths = paths;
|
||||||
|
|
||||||
|
this.sampleRate = inputSampleRate;
|
||||||
|
this.exerciseDuration = exerciseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
this.selfRotationVectorPaths = new Vector3f[2][(int) (exercise.exerciseTimeInSeconds * this.sampleRate)];
|
||||||
|
this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getVectors().length];
|
||||||
|
this.exerciseDuration = exercise.exerciseTimeInSeconds;
|
||||||
|
this.secondsPassed = 0.0D;
|
||||||
|
this.lastTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for checking if the exercise has finished.
|
||||||
|
* @return True if the exercise has finished, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasFinished() {
|
||||||
|
return this.secondsPassed >= this.exerciseDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for starting the listening process
|
||||||
|
* of the motion sensor. This function will create
|
||||||
|
* a new WebSocket server and start listening for
|
||||||
|
* incoming connections.
|
||||||
|
*/
|
||||||
|
public void startListening() {
|
||||||
|
// Create socket server
|
||||||
|
this.server = WebServer.createServer();
|
||||||
|
|
||||||
|
Log.i("MotionProcessor", "Listening for incoming connections.");
|
||||||
|
|
||||||
|
// Check if the socket
|
||||||
|
if (server != null) {
|
||||||
|
// Update event handler to match our functionality.
|
||||||
|
server.setEventHandler(this::parsePacket);
|
||||||
|
this.secondsPassed = 0.0d;
|
||||||
|
this.lastTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for stopping the listening process
|
||||||
|
* of the motion sensor. This function will stop
|
||||||
|
* the WebSocket server.
|
||||||
|
*/
|
||||||
|
public void stopListening() {
|
||||||
|
if (server != null) {
|
||||||
|
server.stop();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for parsing arbitrary packet data.
|
||||||
|
*
|
||||||
|
* @param data The data to parse.
|
||||||
|
*/
|
||||||
|
public void parsePacket(@NotNull String data) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Log.i("MotionProcessor", "Received packet data: " + data);
|
||||||
|
|
||||||
|
JsonElement json = JsonParser.parseString(data);
|
||||||
|
|
||||||
|
if (!json.isJsonObject())
|
||||||
|
return;
|
||||||
|
|
||||||
|
JsonObject object = json.getAsJsonObject();
|
||||||
|
|
||||||
|
String[] required = {
|
||||||
|
"rotationX", "rotationY", "rotationZ",
|
||||||
|
"type",
|
||||||
|
"deviceId"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure all properties are present in the received JSON object
|
||||||
|
for (String s : required) {
|
||||||
|
if (!object.has(s))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the data
|
||||||
|
Vector3f rotation = new Vector3f(object.get("rotationX").getAsFloat(), object.get("rotationY").getAsFloat(), object.get("rotationZ").getAsFloat());
|
||||||
|
int deviceId = object.get("deviceId").getAsInt();
|
||||||
|
String type = object.get("type").getAsString();
|
||||||
|
|
||||||
|
parseRotationVector(rotation, deviceId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i("MotionProcessor", "Failed to parse packet data.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for adding motion data to the processor.
|
||||||
|
*
|
||||||
|
* @param rotation The rotation vector of the motion data.
|
||||||
|
* @param deviceId The device ID of the motion data.
|
||||||
|
*/
|
||||||
|
public void parseRotationVector(Vector3f rotation, int deviceId) {
|
||||||
|
if (deviceId >= 0 && deviceId < selfRotationVectorPaths.length) {
|
||||||
|
|
||||||
|
// Re-calculate time for index calculation
|
||||||
|
secondsPassed = (System.currentTimeMillis() - lastTime) / 1000.0d;
|
||||||
|
lastTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Supposed index of the current rotation vector in the `rotationVectorPaths` array
|
||||||
|
int selfIndex = (int) (secondsPassed * sampleRate);
|
||||||
|
|
||||||
|
motionDataConsumer.accept(rotation, deviceId);
|
||||||
|
if (selfIndex >= selfRotationVectorPaths[deviceId].length || selfIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
selfRotationVectorPaths[deviceId][selfIndex] = rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for getting the current progress of the exercise.
|
||||||
|
* The return value will range between 0.0 and 1.0.
|
||||||
|
*
|
||||||
|
* @return The current progress of the exercise.
|
||||||
|
*/
|
||||||
|
public double getCurrentProgress()
|
||||||
|
{
|
||||||
|
return secondsPassed / exerciseDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for setting the motion data receiver.
|
||||||
|
*
|
||||||
|
* @param consumer The consumer to set.
|
||||||
|
*/
|
||||||
|
public void setInputHandler(IInputHandler consumer) {
|
||||||
|
if (consumer != null)
|
||||||
|
this.motionDataConsumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for getting the error offsets of the user's path compared to the
|
||||||
|
* target path at a given point in time.
|
||||||
|
*
|
||||||
|
* @param sensorId The sensor ID to get the error offsets from.
|
||||||
|
* @param time The time to get the error offsets from.
|
||||||
|
* This value must be >= 0 && <= exerciseTime, otherwise
|
||||||
|
* the error will be 0 by default.
|
||||||
|
* @return A list of error offsets of the motion data compared to the reference path.
|
||||||
|
*/
|
||||||
|
public double getError(int sensorId, float time) {
|
||||||
|
|
||||||
|
// Ensure the sensor ID is within the bounds of the array
|
||||||
|
if (sensorId < 0 || sensorId >= selfRotationVectorPaths.length)
|
||||||
|
return 0.0d;
|
||||||
|
|
||||||
|
// Index of the current rotation vector
|
||||||
|
int targetIndex = (int) ((this.exerciseDuration / this.targetRotationVectorPaths[sensorId].length) * time);
|
||||||
|
int selfIndex = (int) (this.selfRotationVectorPaths[sensorId].length / this.sampleRate * time);
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
return this.selfRotationVectorPaths[sensorId][selfIndex].distance(this.targetRotationVectorPaths[sensorId][targetIndex]);
|
||||||
|
}
|
||||||
|
return 0.0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for getting the average error of the motion data
|
||||||
|
* compared to the reference path.
|
||||||
|
*
|
||||||
|
* @param sensorId The sensor ID to get the error offsets from.
|
||||||
|
* @return The average error of the motion data compared to the reference path.
|
||||||
|
*/
|
||||||
|
public double getAverageError(int sensorId) {
|
||||||
|
double error = 0;
|
||||||
|
for (int i = 0; i < this.exerciseDuration; i++) {
|
||||||
|
error += getError(sensorId, i);
|
||||||
|
}
|
||||||
|
return error / this.exerciseDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float secondsPassed() {
|
||||||
|
return (float) secondsPassed;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,68 +0,0 @@
|
|||||||
package com.example.fitbot.util.processing;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class MotionData {
|
|
||||||
|
|
||||||
// Data of the motion sensor
|
|
||||||
public Vector3f acceleration, rotation;
|
|
||||||
public int sensorId;
|
|
||||||
|
|
||||||
// Delimiter for the data received from the motion sensor
|
|
||||||
private static final String DATA_DELIMITER = ";";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the MotionData class.
|
|
||||||
*
|
|
||||||
* @param accelerationX The acceleration in the X axis in m/s^2.
|
|
||||||
* @param accelerationY The acceleration in the Y axis in m/s^2.
|
|
||||||
* @param accelerationZ The acceleration in the Z axis in m/s^2.
|
|
||||||
* @param rotationX The rotation in the X axis in degrees.
|
|
||||||
* @param rotationY The rotation in the Y axis in degrees.
|
|
||||||
* @param rotationZ The rotation in the Z axis in degrees.
|
|
||||||
* @param sensorId The sensor id.
|
|
||||||
*/
|
|
||||||
public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ, int sensorId) {
|
|
||||||
this(new Vector3f(accelerationX, accelerationY, accelerationZ), new Vector3f(rotationX, rotationY, rotationZ), sensorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the MotionData class.
|
|
||||||
*
|
|
||||||
* @param acceleration The acceleration vector in m/s^2.
|
|
||||||
* @param rotation The rotation vector in degrees.
|
|
||||||
*/
|
|
||||||
public MotionData(Vector3f acceleration, Vector3f rotation, int sensorId) {
|
|
||||||
this.acceleration = acceleration;
|
|
||||||
this.rotation = rotation;
|
|
||||||
this.sensorId = sensorId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for decoding a string into a MotionData object.
|
|
||||||
* This string must contain the data of the motion sensor
|
|
||||||
* separated by the delimiter. (;)
|
|
||||||
*
|
|
||||||
* @param data The string containing the data of the motion sensor.
|
|
||||||
* @return An instance of MotionData.
|
|
||||||
*/
|
|
||||||
public static MotionData decode(String data) {
|
|
||||||
Objects.requireNonNull(data); // Ensure data is not null
|
|
||||||
|
|
||||||
String[] parts = data.split(DATA_DELIMITER);
|
|
||||||
if (parts.length != 7)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return new MotionData(
|
|
||||||
Float.parseFloat(parts[0]),
|
|
||||||
Float.parseFloat(parts[1]),
|
|
||||||
Float.parseFloat(parts[2]),
|
|
||||||
Float.parseFloat(parts[3]),
|
|
||||||
Float.parseFloat(parts[4]),
|
|
||||||
Float.parseFloat(parts[5]),
|
|
||||||
Integer.parseInt(parts[6])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,233 +0,0 @@
|
|||||||
package com.example.fitbot.util.processing;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.example.fitbot.util.path.GesturePath;
|
|
||||||
import com.example.fitbot.util.server.WebServer;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.joml.Matrix3d;
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class MotionProcessor {
|
|
||||||
|
|
||||||
public static final String DELIMITER = ";";
|
|
||||||
|
|
||||||
private final List<Vector3f> relativeLeftPath = new ArrayList<>(); // Relative path of the left motion data
|
|
||||||
private final List<Vector3f> relativeRightPath = new ArrayList<>(); // Relative path of the motion data
|
|
||||||
|
|
||||||
private Vector3f ZERO = new Vector3f(0, 0, 0);
|
|
||||||
|
|
||||||
private final float sampleRate = 1.0f / 10.0F; // samples/second
|
|
||||||
|
|
||||||
private IMotionDataConsumer motionDataConsumer = (p1, p2, p3, p4, p5) -> { };
|
|
||||||
private WebServer server;
|
|
||||||
|
|
||||||
|
|
||||||
public MotionProcessor() {}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for starting the listening process
|
|
||||||
* of the motion sensor. This function will create
|
|
||||||
* a new WebSocket server and start listening for
|
|
||||||
* incoming connections.
|
|
||||||
*/
|
|
||||||
public void startListening() {
|
|
||||||
// Create socket server
|
|
||||||
this.server = WebServer.createServer();
|
|
||||||
|
|
||||||
Log.i("MotionProcessor", "Listening for incoming connections.");
|
|
||||||
|
|
||||||
// Check if the socket
|
|
||||||
if (server != null) {
|
|
||||||
// Update event handler to match our functionality.
|
|
||||||
server.setEventHandler(this::parsePacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for stopping the listening process
|
|
||||||
* of the motion sensor. This function will stop
|
|
||||||
* the WebSocket server.
|
|
||||||
*/
|
|
||||||
public void stopListening() {
|
|
||||||
if (server != null) {
|
|
||||||
server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for parsing arbitrary packet data.
|
|
||||||
*
|
|
||||||
* @param data The data to parse.
|
|
||||||
*/
|
|
||||||
public void parsePacket(@NotNull String data) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
Log.i("MotionProcessor", "Received packet data: " + data);
|
|
||||||
|
|
||||||
JsonElement json = JsonParser.parseString(data);
|
|
||||||
|
|
||||||
if (!json.isJsonObject())
|
|
||||||
return;
|
|
||||||
|
|
||||||
JsonObject object = json.getAsJsonObject();
|
|
||||||
|
|
||||||
String[] required = {
|
|
||||||
"rotationX", "rotationY", "rotationZ",
|
|
||||||
"accelerationX", "accelerationY", "accelerationZ",
|
|
||||||
"type",
|
|
||||||
"deviceId"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure all properties are present in the received JSON object
|
|
||||||
for (String s : required) {
|
|
||||||
if (!object.has(s))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the data
|
|
||||||
Vector3f rotation = new Vector3f(object.get("rotationX").getAsFloat(), object.get("rotationY").getAsFloat(), object.get("rotationZ").getAsFloat());
|
|
||||||
Vector3f acceleration = new Vector3f(object.get("accelerationX").getAsFloat(), object.get("accelerationY").getAsFloat(), object.get("accelerationZ").getAsFloat());
|
|
||||||
int deviceId = object.get("deviceId").getAsInt();
|
|
||||||
String type = object.get("type").getAsString();
|
|
||||||
MotionData motionData = new MotionData(rotation, acceleration, deviceId);
|
|
||||||
|
|
||||||
if (type.equals("calibrate")) {
|
|
||||||
ZERO = getRelativeVector(motionData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addMotionData(motionData);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.i("MotionProcessor", "Failed to parse packet data.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for adding motion data to the processor.
|
|
||||||
*
|
|
||||||
* @param data The motion data to add.
|
|
||||||
*/
|
|
||||||
public void addMotionData(MotionData data) {
|
|
||||||
List<Vector3f> target;
|
|
||||||
if (data.sensorId == 0)
|
|
||||||
target = relativeLeftPath;
|
|
||||||
else target = relativeRightPath;
|
|
||||||
Vector3f previous = target.isEmpty() ? ZERO : target.get(target.size() - 1);
|
|
||||||
Vector3f relativeVector = getRelativeVector(data).add(previous);
|
|
||||||
target.add(relativeVector);
|
|
||||||
motionDataConsumer.accept(relativeVector, data, target.size(), this.sampleRate, data.sensorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for updating the relative path.
|
|
||||||
*
|
|
||||||
* @param relativeRightPath The new relative path.
|
|
||||||
*/
|
|
||||||
public void setRelativePaths(List<Vector3f> relativeLeftPath, List<Vector3f> relativeRightPath) {
|
|
||||||
this.relativeRightPath.clear();
|
|
||||||
this.relativeLeftPath.clear();
|
|
||||||
this.relativeLeftPath.addAll(relativeLeftPath);
|
|
||||||
this.relativeRightPath.addAll(relativeRightPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for setting the motion data receiver.
|
|
||||||
*
|
|
||||||
* @param consumer The consumer to set.
|
|
||||||
*/
|
|
||||||
public void setMotionDataEventHandler(IMotionDataConsumer consumer) {
|
|
||||||
if (consumer != null)
|
|
||||||
this.motionDataConsumer = consumer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for getting the relative vector of the motion data.
|
|
||||||
* This function will calculate the relative position of the motion data
|
|
||||||
* based on its acceleration and rotation vectors. This has to be done since
|
|
||||||
* the acceleration vector is relative to its own rotation vector.
|
|
||||||
*
|
|
||||||
* @param motionData The motion data to calculate the relative vector for.
|
|
||||||
* @return The relative vector of the motion data.
|
|
||||||
*/
|
|
||||||
public Vector3f getRelativeVector(MotionData motionData) {
|
|
||||||
|
|
||||||
// Rotate the acceleration vector back by the rotation vector to make it
|
|
||||||
// perpendicular to the gravity vector, then apply double integration to get the relative position.
|
|
||||||
// s = 1/2 * a * t^2
|
|
||||||
// Step 2: Create rotation matrices for each axis
|
|
||||||
// Step 4: Rotate the acceleration vector
|
|
||||||
|
|
||||||
return motionData.rotation
|
|
||||||
.mul(5);
|
|
||||||
/*return motionData.acceleration
|
|
||||||
.rotateZ(-motionData.rotation.z)
|
|
||||||
.rotateY(-motionData.rotation.y)
|
|
||||||
.rotateX(-motionData.rotation.x)
|
|
||||||
.mul(sampleRate * sampleRate / 2);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for getting the error offsets of the provided path and the
|
|
||||||
* received motion data.
|
|
||||||
*
|
|
||||||
* @param referencePath The reference path to compare the motion data to.
|
|
||||||
* @return A list of error offsets of the motion data compared to the reference path.
|
|
||||||
*/
|
|
||||||
public List<Double> getErrors(GesturePath referencePath) {
|
|
||||||
|
|
||||||
List<Double> errors = new ArrayList<>();
|
|
||||||
for (Vector3f vector : relativeRightPath) {
|
|
||||||
errors.add(referencePath.getError(vector));
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for getting the error of the motion data compared to the reference path.
|
|
||||||
*
|
|
||||||
* @param path The path to compare the motion data to.
|
|
||||||
* @param referencePoint The reference point to compare the motion data to.
|
|
||||||
* @return The error of the motion data compared to the reference path.
|
|
||||||
*/
|
|
||||||
public double getError(GesturePath path, Vector3f referencePoint) {
|
|
||||||
return path.getError(referencePoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for calculating the average error of the motion data
|
|
||||||
* compared to the reference path.
|
|
||||||
*
|
|
||||||
* @param referencePath The reference path to compare the motion data to.
|
|
||||||
* @return The average error of the motion data compared to the reference path.
|
|
||||||
*/
|
|
||||||
public double getAverageError(GesturePath referencePath, int sensorId) {
|
|
||||||
double error = 0;
|
|
||||||
for (Double e : getErrors(referencePath)) {
|
|
||||||
error += e;
|
|
||||||
}
|
|
||||||
return error / Math.max(1, (sensorId == 0 ? relativeLeftPath : relativeRightPath).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for logging the statistics of the motion data.
|
|
||||||
*
|
|
||||||
* @param referencePath The reference path to compare the motion data to.
|
|
||||||
*/
|
|
||||||
public void logStatistics(GesturePath referencePath) {
|
|
||||||
Log.i("MotionProcessor", "Path length: " + relativeRightPath.size());
|
|
||||||
Log.i("MotionProcessor", "Sample rate: " + sampleRate);
|
|
||||||
Log.i("MotionProcessor", "Calibration point: " + ZERO.toString());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -22,7 +22,7 @@ public class WebServer implements Runnable {
|
|||||||
protected IWebServerHandler eventHandler = (input) -> {}; // No-op.
|
protected IWebServerHandler eventHandler = (input) -> {}; // No-op.
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private AtomicBoolean forceClose = new AtomicBoolean(false);
|
private final AtomicBoolean forceClose = new AtomicBoolean(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for creating a new WebSocket server.
|
* Constructor for creating a new WebSocket server.
|
||||||
|
@@ -2,11 +2,12 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<corners
|
<corners
|
||||||
android:radius="20dp"
|
android:radius="40dp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<gradient
|
<gradient
|
||||||
android:startColor="#660000"
|
android:startColor="#990000"
|
||||||
android:endColor="#990000"
|
android:endColor="#FF0000"
|
||||||
android:angle="90"/>
|
android:angle="90"/>
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/darkBlue" />
|
||||||
|
<stroke android:width="4dp" android:color="@color/white" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
</shape>
|
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/midBlue" />
|
||||||
|
<stroke android:width="2dp" android:color="#FF0000" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/lightBlue" />
|
||||||
|
<stroke android:width="2dp" android:color="#FF0000" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
</shape>
|
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||||
|
</vector>
|
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listview_background_shape">
|
|
||||||
<stroke android:width="2dp" android:color="#ff207d94" />
|
|
||||||
<padding android:left="2dp"
|
|
||||||
android:top="2dp"
|
|
||||||
android:right="2dp"
|
|
||||||
android:bottom="2dp" />
|
|
||||||
<corners android:radius="45dp" />
|
|
||||||
<solid android:color="#F0F0F0" />
|
|
||||||
</shape>
|
|
@@ -2,11 +2,12 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<corners
|
<corners
|
||||||
android:radius="30dp"
|
android:radius="25dp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<gradient
|
<gradient
|
||||||
android:startColor="#990000"
|
android:startColor="#990000"
|
||||||
android:endColor="#FF0000"
|
android:endColor="#FF0000"
|
||||||
android:angle="90"/>
|
android:angle="90"/>
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@@ -1,77 +1,113 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:background="@color/red"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.activities.EndScreenActivity">
|
android:background="@color/darkBlue"
|
||||||
|
tools:context=".ui.activities.HelpActivity">
|
||||||
|
|
||||||
<View
|
<LinearLayout
|
||||||
android:id="@+id/myRectangleView"
|
android:layout_width="800dp"
|
||||||
android:layout_width="720dp"
|
android:layout_height="450dp"
|
||||||
android:layout_height="270dp"
|
android:layout_marginStart="80dp"
|
||||||
android:layout_marginStart="320dp"
|
android:layout_marginTop="24dp"
|
||||||
android:layout_marginTop="25dp"
|
android:background="@drawable/help2_background"
|
||||||
android:layout_marginEnd="320dp"
|
android:orientation="vertical"
|
||||||
android:layout_marginBottom="25dp"
|
android:padding="16dp"
|
||||||
android:background="@drawable/rectangle"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/homeButton"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="40dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingVertical="15dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewTitleEndScreen"
|
||||||
|
style="@style/TextStyleTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/afgerond" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="700dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewDescriptionEndScreen"
|
||||||
|
style="@style/TextStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/voltooid"
|
||||||
|
android:textAlignment="center"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="700dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewScoreEndScreen"
|
||||||
|
style="@style/TextStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/score"
|
||||||
|
android:textAlignment="center"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/homeButton"
|
android:id="@+id/homeButtonEndScreen"
|
||||||
style="@style/ButtonStyle"
|
android:layout_width="150dp"
|
||||||
android:layout_width="278dp"
|
android:layout_height="75dp"
|
||||||
android:layout_height="117dp"
|
android:layout_marginEnd="280dp"
|
||||||
android:layout_marginStart="501dp"
|
android:layout_marginBottom="30dp"
|
||||||
android:layout_marginTop="25dp"
|
android:background="@drawable/red_button_gradient"
|
||||||
android:layout_marginEnd="501dp"
|
android:drawableTop="@drawable/ic_baseline_home_48"
|
||||||
android:layout_marginBottom="50dp"
|
android:drawableTint="@color/white"
|
||||||
android:text="Home"
|
android:padding="15dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/continueButton"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/myRectangleView" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/continueButton"
|
android:id="@+id/startButtonEndScreen"
|
||||||
style="@style/ButtonStyle"
|
android:layout_width="150dp"
|
||||||
android:layout_width="280dp"
|
android:layout_height="75dp"
|
||||||
android:layout_height="120dp"
|
android:layout_marginStart="280dp"
|
||||||
android:layout_marginStart="500dp"
|
android:layout_marginBottom="30dp"
|
||||||
android:layout_marginTop="25dp"
|
android:background="@drawable/red_button_gradient"
|
||||||
android:layout_marginEnd="500dp"
|
android:padding="15dp"
|
||||||
android:layout_marginBottom="140dp"
|
android:text="@string/start"
|
||||||
android:text="Continue"
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="30sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/homeButton" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/gefeliciteerdText"
|
|
||||||
style="@style/TextStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Gefeliciteerd"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.155" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/workoutText"
|
|
||||||
style="@style/TextStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="u heeft de work out voltooid"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/myRectangleView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/myRectangleView" />
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
@@ -5,79 +5,53 @@ xmlns:tools="http://schemas.android.com/tools"
|
|||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/black"
|
android:background="@color/darkBlue"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
|
||||||
tools:context=".ui.activities.FitnessActivity"
|
tools:context=".ui.activities.FitnessActivity"
|
||||||
tools:openDrawer="start">
|
tools:openDrawer="start">
|
||||||
|
|
||||||
<VideoView
|
<LinearLayout
|
||||||
android:id="@+id/videoView"
|
android:layout_width="900dp"
|
||||||
android:layout_width="450dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="450dp"
|
android:layout_gravity="center"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginStart="30dp"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:layout_marginEnd="30dp"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:background="@drawable/help2_background"
|
||||||
|
android:orientation="horizontal"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.04"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintVertical_bias="0.2" />
|
|
||||||
|
|
||||||
<com.example.fitbot.ui.components.PersonalMotionPreviewElement
|
<VideoView
|
||||||
android:id="@+id/personalMotionPreviewElement"
|
android:id="@+id/videoView"
|
||||||
android:layout_width="450dp"
|
android:layout_width="400dp"
|
||||||
android:layout_height="450dp"
|
android:layout_height="400dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginStart="20dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
/>
|
||||||
app:layout_constraintHorizontal_bias="0.96"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
<com.example.fitbot.ui.components.PersonalMotionPreviewElement
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:id="@+id/personalMotionPreviewElement"
|
||||||
app:layout_constraintVertical_bias="0.2" />
|
android:layout_width="400dp"
|
||||||
|
android:layout_height="400dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/homeButton"
|
android:id="@+id/homeButtonFitness"
|
||||||
android:layout_width="150dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="75dp"
|
android:layout_height="75dp"
|
||||||
|
android:layout_marginStart="404dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
android:background="@drawable/red_button_gradient"
|
android:background="@drawable/red_button_gradient"
|
||||||
android:text="@string/home"
|
android:drawableTop="@drawable/ic_baseline_home_48"
|
||||||
android:textAllCaps="false"
|
android:drawableTint="@color/white"
|
||||||
android:textColor="@color/white"
|
android:padding="15dp"
|
||||||
android:textSize="30sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.02"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
tools:ignore="SpeakableTextPresentCheck" />
|
||||||
app:layout_constraintVertical_bias="0.968" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/skipButton"
|
|
||||||
android:layout_width="150dp"
|
|
||||||
android:layout_height="75dp"
|
|
||||||
android:background="@drawable/red_button_gradient"
|
|
||||||
android:text="@string/skip"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="30sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.968" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/buttonComplete"
|
|
||||||
android:layout_width="150dp"
|
|
||||||
android:layout_height="75dp"
|
|
||||||
android:background="@drawable/red_button_gradient"
|
|
||||||
android:text="@string/complete"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="30sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.979"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.968" />
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
@@ -4,61 +4,98 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/black"
|
android:background="@color/darkBlue"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".ui.activities.HelpActivity">
|
tools:context=".ui.activities.HelpActivity">
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/myRectangleView"
|
|
||||||
android:layout_width="1075dp"
|
|
||||||
android:layout_height="510dp"
|
|
||||||
android:background="@drawable/rectangle"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.502"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.168"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/homeButton"
|
android:id="@+id/homeButtonHelp"
|
||||||
android:layout_width="200dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="75dp"
|
android:layout_height="75dp"
|
||||||
|
android:layout_marginStart="406dp"
|
||||||
|
android:layout_marginEnd="406dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
android:background="@drawable/red_button_gradient"
|
android:background="@drawable/red_button_gradient"
|
||||||
android:text="@string/home"
|
android:drawableTop="@drawable/ic_baseline_home_48"
|
||||||
android:textAllCaps="false"
|
android:drawableTint="@color/white"
|
||||||
android:textColor="@color/white"
|
android:padding="15dp"
|
||||||
android:textSize="30sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.852" />
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/textView4.2"
|
android:layout_width="800dp"
|
||||||
style="@style/TextStyle"
|
android:layout_height="450dp"
|
||||||
android:layout_width="1053dp"
|
android:layout_marginStart="80dp"
|
||||||
android:layout_height="191dp"
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/uitleg"
|
android:background="@drawable/help2_background"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:orientation="vertical"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:padding="16dp"
|
||||||
app:layout_constraintHorizontal_bias="0.502"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintVertical_bias="0.133" />
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="40dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingVertical="15dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewTitleHelp"
|
||||||
|
style="@style/TextStyleTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/help" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="700dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewStartHelp"
|
||||||
|
style="@style/TextStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/uitlegStart"
|
||||||
|
android:textAlignment="center"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="700dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="@drawable/help_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewHomeHelp"
|
||||||
|
style="@style/TextStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/uitlegHome"
|
||||||
|
android:textAlignment="center"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView4"
|
|
||||||
style="@style/TextStyle"
|
|
||||||
android:layout_width="1053dp"
|
|
||||||
android:layout_height="191dp"
|
|
||||||
android:text="Als je klaar bent kunt u op de COMPLETE knop drukken in het sport scherm en dan kunt u uw punten inzien"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.502"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.482" />
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
@@ -4,9 +4,7 @@
|
|||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#232323"
|
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
|
||||||
tools:context=".ui.activities.MainActivity"
|
tools:context=".ui.activities.MainActivity"
|
||||||
tools:openDrawer="start">
|
tools:openDrawer="start">
|
||||||
|
|
||||||
@@ -26,66 +24,95 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/textView2"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="232dp"
|
android:layout_marginStart="215dp"
|
||||||
android:layout_marginTop="120dp"
|
android:layout_marginTop="120dp"
|
||||||
android:text="@string/welkom_bij_fitbot"
|
android:layout_marginEnd="215dp"
|
||||||
android:textColor="@color/white"
|
android:layout_marginBottom="23dp"
|
||||||
android:textSize="64sp"
|
android:background="@drawable/box_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/startButtonMain"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView3"
|
android:id="@+id/textViewWelkom"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="240dp"
|
android:text="@string/welkom_bij_fitbot"
|
||||||
android:layout_marginTop="200dp"
|
android:textColor="@color/white"
|
||||||
android:text="@string/robot_helpt"
|
android:textSize="64sp" />
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="32sp"
|
<TextView
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:id="@+id/textViewFit"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/robot_helpt"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:translationX="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/startButton"
|
android:id="@+id/startButtonMain"
|
||||||
android:layout_width="200dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="100dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="380dp"
|
android:layout_marginStart="365dp"
|
||||||
android:layout_marginTop="280dp"
|
android:layout_marginTop="315dp"
|
||||||
android:background="@drawable/red_button_gradient"
|
android:layout_marginEnd="365dp"
|
||||||
|
android:layout_marginBottom="22dp"
|
||||||
|
android:background="@drawable/big_red_button_gradient"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="25dp"
|
||||||
|
android:paddingVertical="5dp"
|
||||||
android:text="@string/start"
|
android:text="@string/start"
|
||||||
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="80sp"
|
android:textSize="80sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/helpButtonMain"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.727" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/helpButton"
|
android:id="@+id/helpButtonMain"
|
||||||
android:layout_width="100dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="50dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="428dp"
|
android:layout_marginStart="420dp"
|
||||||
android:layout_marginTop="400dp"
|
android:layout_marginTop="444dp"
|
||||||
android:background="@drawable/darkred_button_gradient"
|
android:layout_marginEnd="420dp"
|
||||||
|
android:background="@drawable/red_button_gradient"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
android:paddingVertical="5dp"
|
||||||
android:text="@string/help"
|
android:text="@string/help"
|
||||||
|
android:textAlignment="center"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="40sp"
|
android:textSize="40sp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imageView2"
|
|
||||||
android:layout_width="200dp"
|
|
||||||
android:layout_height="200dp"
|
|
||||||
android:layout_marginTop="340dp"
|
|
||||||
android:contentDescription="@string/robot_logo"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.258" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageViewRobotLogo"
|
||||||
|
android:layout_width="200dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:contentDescription="@string/robot_logo"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:srcCompat="@drawable/robot_logo_inverted" />
|
app:srcCompat="@drawable/robot_logo_inverted" />
|
||||||
|
|
||||||
|
|
||||||
@@ -96,7 +123,11 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
|
android:background="@color/darkBlue"
|
||||||
app:headerLayout="@layout/header"
|
app:headerLayout="@layout/header"
|
||||||
|
app:itemIconTint="@color/white"
|
||||||
|
app:itemTextColor="@color/white"
|
||||||
|
|
||||||
app:menu="@menu/main_menu" />
|
app:menu="@menu/main_menu" />
|
||||||
|
|
||||||
</android.support.v4.widget.DrawerLayout>
|
</android.support.v4.widget.DrawerLayout>
|
@@ -24,7 +24,7 @@
|
|||||||
android:layout_marginTop="60dp"
|
android:layout_marginTop="60dp"
|
||||||
android:text="FitBot"
|
android:text="FitBot"
|
||||||
android:textSize="48sp"
|
android:textSize="48sp"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/darkBlue"
|
||||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:elevation="8dp">
|
android:elevation="8dp"
|
||||||
|
android:theme="@style/ToolbarNav">
|
||||||
</android.support.v7.widget.Toolbar>
|
</android.support.v7.widget.Toolbar>
|
@@ -12,20 +12,14 @@
|
|||||||
|
|
||||||
<group android:checkableBehavior="single">
|
<group android:checkableBehavior="single">
|
||||||
|
|
||||||
<item android:title="Options">
|
<item
|
||||||
|
android:id="@+id/nav_settings"
|
||||||
<menu>
|
android:icon="@drawable/ic_baseline_settings_48"
|
||||||
<item
|
android:title="Settings" />
|
||||||
android:id="@+id/nav_settings"
|
<item
|
||||||
android:icon="@drawable/ic_baseline_settings_48"
|
android:id="@+id/nav_rate"
|
||||||
android:title="Settings" />
|
android:icon="@drawable/ic_baseline_star_rate_48"
|
||||||
<item
|
android:title="Rate" />
|
||||||
android:id="@+id/nav_rate"
|
|
||||||
android:icon="@drawable/ic_baseline_star_rate_48"
|
|
||||||
android:title="Rate" />
|
|
||||||
</menu>
|
|
||||||
|
|
||||||
</item>
|
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
|
@@ -7,4 +7,11 @@
|
|||||||
<color name="teal_700">#FF018786</color>
|
<color name="teal_700">#FF018786</color>
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
<color name="darkBlue">#1C1C27</color>
|
||||||
|
<color name="midBlue">#24242F</color>
|
||||||
|
<color name="lightBlue">#2C2C37</color>
|
||||||
|
<color name="invertedBackground">#FFFFFF</color>
|
||||||
|
<color name="invertedTextColor">#000000</color>
|
||||||
|
<color name="invertedIconTint">#000000</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||||
<string name="navigation_drawer_close">Open navigation close</string>
|
<string name="navigation_drawer_close">Open navigation close</string>
|
||||||
|
|
||||||
<string name="welkom_bij_fitbot">Welkom bij FitBot</string>
|
<string name="welkom_bij_fitbot">Welkom bij FitBot</string>
|
||||||
<string name="robot_helpt">de robot die helpt om fit te blijven</string>
|
<string name="robot_helpt">de robot die helpt om fit te blijven</string>
|
||||||
|
|
||||||
<string name="start">Start</string>
|
<string name="start">Start</string>
|
||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
<string name="todo">TODO</string>
|
<string name="todo">TODO</string>
|
||||||
@@ -14,7 +16,11 @@
|
|||||||
<string name="skip">Skip</string>
|
<string name="skip">Skip</string>
|
||||||
<string name="complete">Complete</string>
|
<string name="complete">Complete</string>
|
||||||
|
|
||||||
<string name="uitleg">Als je op de startknop drukt komen oefingen op het scherm. Het doel is om die zo goedmogelijk na te doen zodat je punten verzameld. Als je klaar bent kunt u op de COMPLETE knop drukken in het sport scherm en dan kunt u uw punten inzien</string>
|
<string name="uitlegStart">Druk op Start om de oefening te beginnen</string>
|
||||||
<color name="red">#f22b1d</color>
|
<string name="uitlegHome">Ga terug naar het begin scherm door op het huisje te klikken</string>
|
||||||
|
|
||||||
|
<string name="afgerond">Oefeningen afgerond</string>
|
||||||
|
<string name="voltooid">U heeft de oefeningen voltooid! \n Druk op start om nog een sessie te beginnen</string>
|
||||||
|
<string name="score">Score:</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@@ -2,8 +2,11 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="android:colorBackground">@color/darkBlue</item>
|
||||||
<item name="windowNoTitle">true</item>
|
</style>
|
||||||
|
|
||||||
|
<style name="ToolbarNav" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="android:textColorSecondary">@color/white</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ButtonStyle">
|
<style name="ButtonStyle">
|
||||||
@@ -16,10 +19,16 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TextStyle">
|
<style name="TextStyle">
|
||||||
<item name="android:textSize">36sp</item>
|
<item name="android:textSize">25sp</item>
|
||||||
<item name="android:textColor">#000000</item>
|
<item name="android:textColor">#FFFFFF</item>
|
||||||
<item name= "android:textStyle">bold</item>
|
<item name= "android:textStyle">bold</item>
|
||||||
|
<item name="android:padding">6dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="TextStyleTitle">
|
||||||
|
<item name="android:textSize">50sp</item>
|
||||||
|
<item name="android:textColor">#D3D3D3</item>
|
||||||
|
<item name= "android:textStyle">bold</item>
|
||||||
|
<item name="android:padding">6dp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
@@ -0,0 +1,29 @@
|
|||||||
|
package com.example.fitbot;
|
||||||
|
|
||||||
|
import com.example.fitbot.exercise.EMuscleGroup;
|
||||||
|
import com.example.fitbot.exercise.Exercise;
|
||||||
|
import com.example.fitbot.exercise.ExerciseManager;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DatabaseFetchingTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDatabaseFetching() {
|
||||||
|
Exercise exercise = ExerciseManager.retrieveExercise();
|
||||||
|
assert exercise != null;
|
||||||
|
System.out.println("\n---------------------------------");
|
||||||
|
System.out.println("Exercise:");
|
||||||
|
System.out.println("Name: " + exercise.title);
|
||||||
|
System.out.println("Description: " + exercise.description);
|
||||||
|
System.out.println("Muscle Group: " + exercise.muscleGroup);
|
||||||
|
System.out.println("Image URL: " + exercise.imageUrl);
|
||||||
|
System.out.println("Video URL: " + exercise.videoUrl);
|
||||||
|
System.out.println("Exercise Time: " + exercise.exerciseTimeInSeconds);
|
||||||
|
System.out.println("\n---------------------------------");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
62
docs/documentation/diagrams/infrastructure.md
Normal file
62
docs/documentation/diagrams/infrastructure.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Infrastructure UML
|
||||||
|
|
||||||
|
``` mermaid
|
||||||
|
classDiagram
|
||||||
|
|
||||||
|
Raspberry pi --> NodeJS
|
||||||
|
Raspberry pi --> Database
|
||||||
|
NodeJS --> Androidapp : getExerciseData (Wifi, Rest API)
|
||||||
|
Database <--> NodeJS : Database queries
|
||||||
|
|
||||||
|
|
||||||
|
ESP8266 --> Androidapp : getRotationalData (Wifi)
|
||||||
|
namespace Server {
|
||||||
|
class Raspberry pi {
|
||||||
|
+MariaDB
|
||||||
|
+Apache2
|
||||||
|
+NodeJS
|
||||||
|
Database()
|
||||||
|
Webserver()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
+ExerciseID
|
||||||
|
+ExerciseName
|
||||||
|
+ExerciseDescription
|
||||||
|
+ExerciseVideo
|
||||||
|
+GyroCoordinates
|
||||||
|
+MuscleGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeJS {
|
||||||
|
+MariaDB
|
||||||
|
GetRandomExercise()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Pepper {
|
||||||
|
class Androidapp {
|
||||||
|
+Java
|
||||||
|
+Android SDK
|
||||||
|
+QiSDK
|
||||||
|
motionProcessing()
|
||||||
|
robotMovement()
|
||||||
|
showVideo()
|
||||||
|
fitnessCycle()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Hardware {
|
||||||
|
class ESP8266{
|
||||||
|
+RotationalX
|
||||||
|
+RotationalY
|
||||||
|
+RotationalZ
|
||||||
|
Gyroscope()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@@ -2,25 +2,34 @@
|
|||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
For this project we want to design an embedded system that can track a users position. We want to track their current position on the ground and see how they are shifting their weight. This system will be used to track their position to determine if a user is doing the exercises correctly.
|
For this project we want to design an embedded system that can track a users position. We want to track their current position on the ground. This system will be used to track their position to determine if a user is doing the exercises correctly.
|
||||||
|
|
||||||
## Objectives
|
## Objectives
|
||||||
|
|
||||||
* Design an embedded system that can track user position.
|
- Design an embedded system that can track user position.
|
||||||
* Develop an algorithm to process the data from the Wii Fit Board and determine the user's position.
|
- Develop an algorithm to process the data from the sensor and determine the user's position.
|
||||||
|
- Sync the code to the current task for the user.
|
||||||
|
|
||||||
## Research and Analysis
|
## Research and Analysis
|
||||||
|
|
||||||
### Choosing the Wii Fit Board
|
### Choosing the sensor
|
||||||
|
|
||||||
For this project we have chosen the Wii Fit Board as our primary sensor. The Wii Fit Board is a balance board that can measure a user's weight and center of balance. It is a low-cost sensor that is easy to interface with a microcontroller. The Wii Fit Board communicates over Bluetooth, which makes it easy to connect to a microcontroller with Bluetooth capabilities.
|
For this project we have chosen LDR's as our primary sensor. The LDR's will be placed on the ground in a board and the user will stand on top of the board. The LDR's will be used to track the user's position. The LDR's will be connected to the esp32s3 microcontroller and the data will be processed to determine the user's position.
|
||||||
|
|
||||||
|
We have chosen this sensor since it's one of the easiest and cheapest solutions to our problem. Other sensors like pressure sensors, accelerometers, and Wii Balance Board are either too expensive, not the most optimal for the task, or hard to integrate with other systems.
|
||||||
|
|
||||||
### Alternative Solutions
|
### Alternative Solutions
|
||||||
|
|
||||||
There are other sensors that can be used for position tracking, such as pressure sensors or accelerometers. However, these sensors are more expensive and may require additional processing to determine the user's position. The Wii Fit Board provides a simple and cost-effective solution for position tracking.
|
There are other sensors that can be used for position tracking, such as pressure sensors, Wii Balance Board or accelerometers. However, these sensors are either too expensive, not the most optimal for the task or hard to integrate with other systems.
|
||||||
|
|
||||||
Example of other sensors that can be used for position tracking:
|
Example of other sensors that can be used for position tracking:
|
||||||
|
|
||||||
|
Wii Balance Board:
|
||||||
|
- Description: The Wii Balance Board is a balance board that can measure a user's weight and center of balance.
|
||||||
|
- Pros: Low-cost.
|
||||||
|
- Cons: Very hard to intergrate with other systems.
|
||||||
|
- Cost: ~ 20 euros (https://www.amazon.nl/Nintendo-Wii-Balance-Board-Wii/dp/B0013E9HP6)
|
||||||
|
|
||||||
Pressure sensors:
|
Pressure sensors:
|
||||||
- Description: Pressure sensors can be used to measure the force applied by the user on the ground. By measuring the pressure distribution, the user's position can be determined.
|
- Description: Pressure sensors can be used to measure the force applied by the user on the ground. By measuring the pressure distribution, the user's position can be determined.
|
||||||
- Pros: High accuracy, can measure force applied by the user.
|
- Pros: High accuracy, can measure force applied by the user.
|
||||||
@@ -42,36 +51,31 @@ To be added
|
|||||||
### Hardware
|
### Hardware
|
||||||
|
|
||||||
The hardware of the system will consist of the following components:
|
The hardware of the system will consist of the following components:
|
||||||
- Wii Fit Board: The primary sensor for position tracking.
|
- LDR: The sensor that will be used to track the user's position based on the light intensity.
|
||||||
- Pepper: The controller that will process the data from the Wii Fit Board.
|
- ESP32S3: The microcontroller that will process the data from the LDR.
|
||||||
|
- Pepper: The controller that will recieve the processed data from the ESP32S3 and will sync the data to the current task for the user.
|
||||||
|
|
||||||
|
#### Connection diagram
|
||||||
|
|
||||||
|
To be added
|
||||||
|
|
||||||
### Software
|
### Software
|
||||||
|
|
||||||
The software of the system will consist of the following:
|
To be added
|
||||||
- Wiiboard-simple: A library that will be used to transfer data from the Wii Fit Board to pepper.
|
|
||||||
- Position Tracking Algorithm: An algorithm that will process the sensor data and determine the user's position.
|
|
||||||
|
|
||||||
### Integration
|
### Integration
|
||||||
|
|
||||||
The Wii Fit Board will be connected to Pepper using the Wiiboard-simple library. The library will be used to read the sensor data from the Wii Fit Board and transfer it to Pepper. The position tracking algorithm will process the sensor data and determine the user's position.
|
To be added
|
||||||
|
|
||||||
Challenge:
|
|
||||||
- Connecting to the wii fit board. It is not possible to connect directly to the Wii Fit Board, it is necessary to use a library that can interpret the data sent by the Wii Fit Board.
|
|
||||||
- The Wii Fit Balance Board sends data in a specific format. To interpret this data, it's necessary to understand the format and how to convert it to a usable format.
|
|
||||||
- The Wii Fit Balance Board uses Bluetooth 2.0 to communicate. Pepper uses Bluetooth 4.0 this means that there might be compatibility issues/latancy issues.
|
|
||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
### Prototyping
|
### Prototyping
|
||||||
|
|
||||||
To start the implementation of the system, we will create a prototype that will read the sensor data from the Wii Fit Board and send it to your computer. Once we have the data, we will develop the position tracking algorithm to determine the user's position. After that, the algorithm will be integrated with pepper.
|
To be added
|
||||||
|
|
||||||
### Testing and Validation
|
### Testing and Validation
|
||||||
|
|
||||||
Tests:
|
To be added
|
||||||
- Test the prototype to ensure that it can read the sensor data from the Wii Fit Board.
|
|
||||||
- Test the position tracking algorithm to ensure that it can determine the user's position accurately.
|
|
||||||
- Test the integrated system to ensure that it can track the user's position in real-time.
|
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
@@ -79,9 +83,8 @@ To be added
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
[Wiiboard lib](https://code.google.com/archive/p/wiiboard-simple/wikis/Documentation.wiki)
|
[Bluetooth Discovery](https://developer.android.com/develop/connectivity/bluetooth/find-bluetooth-devices)
|
||||||
[BlueSoil](https://advanti-lab.sb.dfki.de/?page_id=64)
|
|
||||||
[FitScales](https://github.com/paulburton/fitscales)
|
## Appendices
|
||||||
[WiiRemoteJ](https://github.com/micromu/WiiRemoteJ)
|
|
||||||
[Wiibrew Wiimote](https://wiibrew.org/wiki/Wiimote)
|
To be added
|
||||||
[Wiibrew Balance Board](https://wiibrew.org/wiki/Wii_Balance_Board)
|
|
@@ -294,13 +294,13 @@ To do
|
|||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
-
|
- skill ontwikkeling plan
|
||||||
|
|
||||||
**23 May**
|
**23 May**
|
||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- NodeJs opzetten
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
@@ -310,37 +310,30 @@ Done
|
|||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- Pi fixen
|
||||||
|
- Sprint review
|
||||||
|
- Retrospective
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
-
|
- Pi fixen
|
||||||
|
- Sprint review
|
||||||
|
- Retrospective
|
||||||
|
|
||||||
**25 May**
|
**25 May**
|
||||||
|
|
||||||
To do
|
- Weekend
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
Done
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
**26 May**
|
**26 May**
|
||||||
|
|
||||||
To do
|
- Weekend / Betoog
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
Done
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
**27 May**
|
**27 May**
|
||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- Betoog
|
||||||
|
- NodeJs opzetten
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
@@ -350,27 +343,37 @@ Done
|
|||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- Betoog
|
||||||
|
- NodeJs opzetten
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
-
|
- Betoog
|
||||||
|
- NodeJs opzetten
|
||||||
|
|
||||||
**29 May**
|
**29 May**
|
||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- Connectie MariaDB fixen
|
||||||
|
- Ui Style aanpassen en overal het zelfde maken
|
||||||
|
- Ui in het Nederlands
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
-
|
- Connectie MariaDB fixen
|
||||||
|
- Ui Styke aanpassen en overal het zelfde maken
|
||||||
|
- Ui in het Nederlands
|
||||||
|
|
||||||
**30 May**
|
**30 May**
|
||||||
|
|
||||||
To do
|
To do
|
||||||
|
|
||||||
-
|
- Ui end screen updaten
|
||||||
|
- Data ophalen uit database in Android App
|
||||||
|
- Data verwerken in App (oefeningen)
|
||||||
|
- Expert voorbereiden
|
||||||
|
- Skill ontwikkeling plan afmaken
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
# Expert 3 Sprint 3
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## K1: Je hebt object georiënteerde software gemaakt die samenwerkt met een database.
|
||||||
|
|
||||||
|
Voor het bewijs van algemene kennis over K1 zie [Expert review 2 K1](../expertReview/expert2sprint2.md#K1:-Je-hebt-object-georiënteerde-software-gemaakt-die-samenwerkt-met-een-database.).
|
||||||
|
|
||||||
|
|
||||||
|
Deze sprint ben ik bezig geweest met:
|
||||||
|
|
||||||
|
- Functionaliteit van de database
|
||||||
|
- Functionaliteit van de server
|
||||||
|
- Data ophalen uit de database en weergeven in de app
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## K3: Je hebt een infrastructuur ontworpen en gebouwd volgens zelf-gedefinieerde vereisten.
|
||||||
|
|
||||||
|
Feedback verwerkt (diagrammen)
|
||||||
|
|
||||||
|
Infrastructuur beschreven met problemen en oplossingen [Infrastuctuur](https://muupooviixee66-propedeuse-hbo-ict-onderwijs-2023-178fb5f296aa35.dev.hihva.nl/documentation/database/infrastructure/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## K4: Je ontwerpt een embedded systeem op basis van gegeven hardware. & K5: Je kan software schrijven voor een intelligente controller voorzien van actuatoren en sensoren.
|
||||||
|
|
||||||
|
Research naar hardware en software voor de controller
|
Reference in New Issue
Block a user