diff --git a/.vscode/settings.json b/.vscode/settings.json
index c5f3f6b..0e14d8e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,3 @@
{
- "java.configuration.updateBuildConfiguration": "interactive"
+ "java.configuration.updateBuildConfiguration": "disabled"
}
\ No newline at end of file
diff --git a/code/arduino/Movement-sensor-code/Connectivity.cpp b/code/arduino/Movement-sensor-code/Connectivity.cpp
index a1c30c4..bfc3c04 100644
--- a/code/arduino/Movement-sensor-code/Connectivity.cpp
+++ b/code/arduino/Movement-sensor-code/Connectivity.cpp
@@ -4,29 +4,53 @@ void Connectivity::connectWiFi(char* ssid, char* pass){
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
- Serial.println("connecting to wifi");
delay(1000);
}
- Serial.println(WiFi.localIP());
}
-void Connectivity::websocketSetup(char* ip, uint16_t port, char* adress){
- //ws server address, port and URL
- webSocket.begin(ip , port, adress);
- // try every 500 again if connection has failed
- webSocket.setReconnectInterval(500);
-}
+// void Connectivity::websocketSetup(char* ip, uint16_t port, char* adress){
+// //ws server address, port and URL
+// webSocket.begin(ip , port, adress);
+// // try every 500 again if connection has failed
+// webSocket.setReconnectInterval(500);
+// }
-void Connectivity::sendData(float roll, float pitch, float yaw){
- String message = "{\"Sensor\": 1, \"roll\":\"" + String(roll) + "\",\"pitch\":\"" + String(pitch) + "\",\"yaw\":\"" + String(yaw) + "\"}";
- webSocket.sendTXT(message);
+// void Connectivity::sendData(float roll, float pitch, float yaw){
+// String message = "{\"Sensor\": 1, \"roll\":\"" + String(roll) + "\",\"pitch\":\"" + String(pitch) + "\",\"yaw\":\"" + String(yaw) + "\"}";
+// webSocket.sendTXT(message);
+// }
+
+const char* getServerURL = "http://145.92.8.132:443/get-ip";
+String ipAddress = "";
+
+String Connectivity::fetchIPAddress() {
+ if (WiFi.status() == WL_CONNECTED) {
+ HTTPClient http;
+ WiFiClient client;
+ http.begin(client, getServerURL);
+
+ int httpCode = http.GET();
+ if (httpCode > 0) {
+ if (httpCode == HTTP_CODE_OK) {
+ ipAddress = http.getString();
+ }
+ } else {
+ Serial.printf("GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
+ }
+
+ http.end();
+ } else {
+ Serial.println("WiFi not connected");
+ }
+ return ipAddress; // Add this return statement
}
/** Send a POST request to a server with provided data */
int Connectivity::httpPost(const char *serverAddress, const char *serverSubPath, const unsigned short serverPort,
const char *data, const size_t dataLength, const char *contentType)
{
- if ( wifi_client.connect(serverAddress, serverPort)) {
+ WiFiClient wifi_client; // Ensure WiFiClient is declared and initialized
+ if (wifi_client.connect(serverAddress, serverPort)) {
wifi_client.printf("POST %s HTTP/1.1\r\n", serverSubPath);
wifi_client.printf("Content-Type: %s\r\n", contentType);
wifi_client.printf("Content-Length: %d\r\n", dataLength);
@@ -37,4 +61,4 @@ int Connectivity::httpPost(const char *serverAddress, const char *serverSubPath,
}
return 1;
-}
\ No newline at end of file
+}
diff --git a/code/arduino/Movement-sensor-code/Connectivity.h b/code/arduino/Movement-sensor-code/Connectivity.h
index 90742aa..6de0109 100644
--- a/code/arduino/Movement-sensor-code/Connectivity.h
+++ b/code/arduino/Movement-sensor-code/Connectivity.h
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -18,12 +19,12 @@ public:
void websocketSetup(char* ip, uint16_t port, char* adress);
void sendData(float roll, float pitch, float yaw);
int httpPost(const char *serverAddress, const char *serverSubPath, const unsigned short serverPort, const char *data, const size_t dataLength, const char *contentType);
-
+ String fetchIPAddress();
private:
ESP8266WiFiMulti wifi;
WiFiClient wifi_client;
- WebSocketsClient webSocket;
+ // WebSocketsClient webSocket;
};
diff --git a/code/arduino/Movement-sensor-code/Movement-sensor-code.ino b/code/arduino/Movement-sensor-code/Movement-sensor-code.ino
index 89ac75b..061f12d 100644
--- a/code/arduino/Movement-sensor-code/Movement-sensor-code.ino
+++ b/code/arduino/Movement-sensor-code/Movement-sensor-code.ino
@@ -1,42 +1,40 @@
#include "headerFile.h"
-// SensorManager::Rotation offset;
-#define BUFFER_SIZE 1024
-
-#define DEVICE_ID 1
-
-char *buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
-
void setup() {
- Serial.begin(9600);
- // Serial.println("startup");
//connect to internet and start sensor
connectivity.connectWiFi(ssid, pass);
sensorManager.sensorSetup();
+ Serial.begin(9600);
}
+unsigned long lastTime = 0; // will store the last time the code was run
+
void loop() {
SensorManager::eulerAngles eulerRotation = sensorManager.getEulerAngles();
- SensorManager::acceleration rotationAcceleration = sensorManager.getAcelleration();
- unsigned long lastTime = 0; // will store the last time the code was run
+ // SensorManager::acceleration rotationAcceleration = sensorManager.getAcelleration();
+
+struct acceleration {
+ float x = 9;
+ float y = 9;
+ float z = 9;
+} accelData;
unsigned long currentTime = millis();
if (currentTime - lastTime >= 100) { // 100 ms has passed
memset(buffer, 0, BUFFER_SIZE);
sprintf(
buffer,
- "{\"deviceId\": %d, \"rotationX\": %d, \"rotationY\": %d, \"rotationZ\": %d, \"accelerationX\": %d, \"accelerationY\": %d, \"accelerationZ\": %d, \"type\": %s}",
+ "{\"deviceId\": %d, \"rotationX\": %f, \"rotationY\": %f, \"rotationZ\": %f, \"accelerationX\": %f, \"accelerationY\": %f, \"accelerationZ\": %f, \"type\": %s}",
DEVICE_ID,
eulerRotation.roll,
eulerRotation.pitch,
eulerRotation.yaw,
- rotationAcceleration.x,
- rotationAcceleration.y,
- rotationAcceleration.z,
+ accelData.x,
+ accelData.y,
+ accelData.z,
"data");
- // Serial.println(connectivity.httpPost("192.168.137.45", "/", 3445, message.c_str(), message.length(), "json"));
- // Serial.println(message);
- connectivity.httpPost("192.168.137.45", "/", 3445, buffer, strlen(buffer), "application/json");
+ // %d = int, %f = floatation, %s = string
+ connectivity.httpPost(connectivity.fetchIPAddress(), "/", 3445, buffer, strlen(buffer), "application/json");
lastTime = currentTime;
}
}
diff --git a/code/arduino/Movement-sensor-code/SensorManager.cpp b/code/arduino/Movement-sensor-code/SensorManager.cpp
index 77836e4..7fee39d 100644
--- a/code/arduino/Movement-sensor-code/SensorManager.cpp
+++ b/code/arduino/Movement-sensor-code/SensorManager.cpp
@@ -9,15 +9,12 @@ void SensorManager::sensorSetup() {
//wait for the sensor to start before continue
if (myIMU.begin() == false) {
delay(1000);
- // Serial.println(".");
}
//start sensorfunction and start autocalibration
//once calibration is enabled it attempts to every 5 min
-
myIMU.enableGyroIntegratedRotationVector(100); //send data every 100ms
- myIMU.enableAccelerometer(100); //Send data update every 100ms
- Serial.println(F("magnetometer rotation enabled"));
- myIMU.enableStepCounter(500); //Send data update every 500ms
+ // myIMU.enableAccelerometer(100); //Send data update every 100ms
+ // myIMU.enableStepCounter(500); //Send data update every 500ms
}
//get sensordata
SensorManager::RotationQuintillions SensorManager::getQuintillions() {
diff --git a/code/arduino/Movement-sensor-code/headerFIle.h b/code/arduino/Movement-sensor-code/headerFIle.h
index b7b8220..8e3480e 100644
--- a/code/arduino/Movement-sensor-code/headerFIle.h
+++ b/code/arduino/Movement-sensor-code/headerFIle.h
@@ -8,6 +8,10 @@ Connectivity connectivity;
WebSocketsClient webSocket;
#define USE_SERIAL Serial
-
#define ssid "1235678i"
#define pass "12345678"
+#define BUFFER_SIZE 1024
+#define DEVICE_ID 1
+#define IP_ADDRESS "192.168.137.12"
+
+char *buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
diff --git a/code/arduino/bluetoothEsp/bluetoothEsp.ino b/code/arduino/bluetoothEsp/bluetoothEsp.ino
deleted file mode 100644
index 0e0eaaf..0000000
--- a/code/arduino/bluetoothEsp/bluetoothEsp.ino
+++ /dev/null
@@ -1,33 +0,0 @@
-#include
-#include
-
-// Define the service UUID
-#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
-
-// Define the characteristic UUID
-#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
-
-void setup() {
- // Create a BLE server
- BLEServer *pServer = BLEDevice::createServer();
-
- // Create a BLE service
- BLEService *pService = pServer->createService(SERVICE_UUID);
-
- // Create a BLE characteristic
- BLECharacteristic *pCharacteristic = pService->createCharacteristic(
- CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ);
-
- // Set the characteristic value
- pCharacteristic->setValue("Hello, Bluetooth!");
-
- // Start the service
- pService->start();
-
- // Start advertising the service
- pServer->getAdvertising()->start();
-}
-
-void loop() {
- // Nothing to do here
-}
\ No newline at end of file
diff --git a/code/web/incoming_request_handlers.js b/code/server/incoming_request_handlers.js
similarity index 85%
rename from code/web/incoming_request_handlers.js
rename to code/server/incoming_request_handlers.js
index 6049b3b..6fa0f41 100644
--- a/code/web/incoming_request_handlers.js
+++ b/code/server/incoming_request_handlers.js
@@ -1,4 +1,3 @@
-
/**
*
* @param {Request} request The incoming request
@@ -14,7 +13,7 @@ function handleIncoming(request, response, app, pool)
if (!request.hasOwnProperty('uid') || typeof request.uid !== 'number')
{
- query = 'SELECT * FROM Exercise';
+ query = 'SELECT * FROM Exercise ORDER BY RAND() LIMIT 1';
} else parameters.push(request.uid);
// Acquire database connection
@@ -33,13 +32,17 @@ function handleIncoming(request, response, app, pool)
// Send back the data in the right format
let converted = rows.map(row => {
return {
+ exerciseId: row.ExerciseID,
name: row.Name,
- description: row.Description,
muscleGroup: row.MuscleGroup,
+ shortDescription: row.ShortDescription,
+ description: row.Description,
imageUrl: row.ImageURL,
- videoUrl: row.VideoURL
+ videoUrl: row.VideoURL,
+ path: row.Path,
+ duration: row.Duration
};
- });
+ })[0];
response
.status(200)
diff --git a/code/server/ipSender/index.js b/code/server/ipSender/index.js
new file mode 100644
index 0000000..dc68c2d
--- /dev/null
+++ b/code/server/ipSender/index.js
@@ -0,0 +1,22 @@
+const express = require('express');
+const app = express();
+
+app.use(express.json()); // for parsing application/json
+
+let ipAddress = ''; // to store the received IP address
+
+// endpoint to receive an IP address from an external source
+app.post('/set-ip', (req, res) => {
+ ipAddress = req.body.ip;
+ console.log('IP address received:', ipAddress);
+});
+
+// endpoint for the ESP32 to fetch the IP address
+app.get('/get-ip', (req, res) => {
+ res.json({ ip: ipAddress });
+ console.log('IP address sent to ESP32');
+});
+
+app.listen(42069, () => {
+ console.log('Server is running on port 42069');
+});
\ No newline at end of file
diff --git a/code/web/server.js b/code/server/server.js
similarity index 61%
rename from code/web/server.js
rename to code/server/server.js
index 886846e..c31eded 100644
--- a/code/web/server.js
+++ b/code/server/server.js
@@ -21,6 +21,20 @@ const pool = mariadb.createPool(databaseCredentials);
// Register incoming HTTP request handlers
require('./incoming_request_handlers')(app, pool);
+let ipAddress = ''; // to store the received IP address
+
+// endpoint to receive an IP address from an external source
+app.post('/set-ip', (req, res) => {
+ ipAddress = req.body.ip;
+ console.log('IP address received:', ipAddress);
+});
+
+// endpoint for the ESP32 to fetch the IP address
+app.get('/get-ip', (req, res) => {
+ res.json({ ip: ipAddress });
+ console.log('IP address sent to ESP32');
+});
+
// Start server
app.listen(serverPort, () => {
console.log(`Server running on port ${serverPort}`);
diff --git a/code/server/test/config.js b/code/server/test/config.js
new file mode 100644
index 0000000..87c69b0
--- /dev/null
+++ b/code/server/test/config.js
@@ -0,0 +1,8 @@
+// config.js
+module.exports = {
+ host: '127.0.0.1',
+ user: 'fitbot',
+ password: 'fitbot123',
+ database: 'fitbot',
+ port: 3306, // Default MariaDB port
+ };
diff --git a/code/server/test/testConnection.js b/code/server/test/testConnection.js
new file mode 100644
index 0000000..4eb953a
--- /dev/null
+++ b/code/server/test/testConnection.js
@@ -0,0 +1,35 @@
+const express = require('express');
+const mariadb = require('mariadb');
+const config = require('./config');
+
+const app = express();
+const port = 443; // Use port 443
+
+const pool = mariadb.createPool({
+ host: config.host,
+ user: config.user,
+ password: config.password,
+ database: config.database,
+ connectionLimit: 5,
+ acquireTimeout: 30000, // Increase timeout to 30 seconds
+});
+
+app.get('/', async (req, res) => {
+ let conn;
+ try {
+ conn = await pool.getConnection();
+ console.log("Connected to MariaDB!");
+ const rows = await conn.query("SELECT * FROM Exercise ORDER BY RAND() LIMIT 1");
+ console.log(rows); // [{val: 1}]
+ res.json(rows); // Send the result as JSON response
+ } catch (err) {
+ console.error("Unable to connect to MariaDB:", err);
+ res.status(500).send("Unable to connect to MariaDB");
+ } finally {
+ if (conn) conn.release(); // Release the connection back to the pool
+ }
+});
+
+app.listen(port, '145.92.8.132', () => {
+ console.log(`Server is listening on http://145.92.8.132:${port}`);
+});
diff --git a/code/src/Fitbot/.idea/misc.xml b/code/src/Fitbot/.idea/misc.xml
index 4496bac..b1965d6 100644
--- a/code/src/Fitbot/.idea/misc.xml
+++ b/code/src/Fitbot/.idea/misc.xml
@@ -16,25 +16,37 @@
-
+
+
+
+
+
+
+
-
+
+
+
+
+
-
-
+
+
+
+
diff --git a/code/src/Fitbot/app/build.gradle b/code/src/Fitbot/app/build.gradle
index e63614b..1348df8 100644
--- a/code/src/Fitbot/app/build.gradle
+++ b/code/src/Fitbot/app/build.gradle
@@ -38,9 +38,6 @@ dependencies {
implementation 'org.joml:joml:1.10.5'
implementation 'com.google.code.gson:gson:2.8.6'
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.espresso:espresso-core:3.0.2'
implementation 'com.aldebaran:qisdk:1.7.5'
diff --git a/code/src/Fitbot/app/src/main/AndroidManifest.xml b/code/src/Fitbot/app/src/main/AndroidManifest.xml
index 326887c..32bdaaf 100644
--- a/code/src/Fitbot/app/src/main/AndroidManifest.xml
+++ b/code/src/Fitbot/app/src/main/AndroidManifest.xml
@@ -10,19 +10,21 @@
+
+
+
+
-
-
+ * Before any of the functions in this class can be used, the Pepper class needs a QiContext to be
+ * set. This is retrieved in the `onRobotFocusGained` method of the RobotLifecycleCallbacks
+ * interface, and can then provided using the `provideContext` method.
+ *
+ */
+public class Pepper {
+
+ // Queue containing all Pepper actions that need to be executed
+ // This is to prevent the app from crashing when attempting to perform multiple actions at once
+ private static final ConcurrentLinkedQueue pepperActionEventQueue =
+ new ConcurrentLinkedQueue<>();
+
+ private static final AtomicBoolean isAnimating = new AtomicBoolean(false);
+ private static final AtomicBoolean isSpeaking = new AtomicBoolean(false);
+
+ private static final Locale DEFAULT_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS);
+
+ /**
+ * The latest QiContext used by Pepper.
+ * This can be publicly accessed to get the latest QiContext used by Pepper.
+ */
+ public static QiContext latestContext = null;
+
+ /**
+ * Make the Pepper robot speak a phrase.
+ * This function adds a SpeechEvent to the event queue,
+ * and will speak the phrase once Pepper is ready to process it.
+ *
+ * @param phrase The phrase to speak
+ */
+ public static void say(String phrase) {
+ addToEventQueue(new PepperSpeechEvent(phrase, DEFAULT_LOCALE));
+ }
+
+ /**
+ * Make the Pepper robot perform an animation.
+ * This function adds an AnimationEvent to the event queue,
+ * and will perform the animation once Pepper is ready to process it.
+ *
+ * @param animationName The name of the animation to play
+ */
+ public static void animate(String animationName) {
+ addToEventQueue(new PepperAnimationEvent(animationName));
+ }
+
+ /**
+ * Add an event to the event queue.
+ * The event will be executed once Pepper is ready to process it.
+ *
+ * @param event The event to add to the queue
+ */
+ public static void addToEventQueue(AbstractPepperActionEvent event) {
+ pepperActionEventQueue.add(event);
+ processEventQueue();
+ }
+
+ /**
+ * Process the event queue.
+ * This method will process the next event in the queue if Pepper is not currently
+ * speaking or animating.
+ * This prevents multiple actions from being executed at the same time, which can
+ * cause the application to crash.
+ */
+ private static synchronized void processEventQueue() {
+ if (!pepperActionEventQueue.isEmpty()) {
+ AbstractPepperActionEvent event = pepperActionEventQueue.poll();
+
+ // Prevent null pointer exceptions
+ if (event == null || latestContext == null)
+ return;
+
+ switch (event.getAction()) {
+ /* Event for speaking a phrase **/
+ case ACTION_SPEAK:
+ if (!(event instanceof PepperSpeechEvent) || isSpeaking.get())
+ break;
+
+ PepperSpeechEvent speechEvent = (PepperSpeechEvent) event;
+ isSpeaking.set(true);
+ speechEvent
+ .getSay(latestContext)
+ .async()
+ .run()
+ .andThenConsume(future -> {
+ isSpeaking.set(false);
+ processEventQueue();
+ });
+ break;
+
+ /* Event for animating the robot **/
+ case ACTION_ANIMATE:
+ if (!(event instanceof PepperAnimationEvent) || isAnimating.get())
+ break;
+
+ PepperAnimationEvent animationEvent = (PepperAnimationEvent) event;
+ animationEvent
+ .getAnimation(latestContext)
+ .async()
+ .run()
+ .andThenConsume(future -> {
+ isAnimating.set(false);
+ processEventQueue();
+ });
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+ }
+ }
+
+ /**
+ * Function for providing the latest QiContext to Pepper.
+ *
+ * @param context The QiContext to use.
+ * This can be null if the context is not available.
+ * @param origin The origin of the context.
+ * This parameter is to prevent other classes from
+ * unnecessarily providing a context, or setting
+ * the context to null.
+ */
+ public static void provideContext(QiContext context, Class extends RobotLifecycleCallbacks> origin) {
+ latestContext = context;
+ if (context != null) {
+ processEventQueue();
+ }
+ }
+
+}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java
new file mode 100644
index 0000000..455be28
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperAnimationEvent.java
@@ -0,0 +1,65 @@
+package com.example.fitbot.pepper;
+
+import com.aldebaran.qi.sdk.QiContext;
+import com.aldebaran.qi.sdk.builder.AnimateBuilder;
+import com.aldebaran.qi.sdk.builder.AnimationBuilder;
+import com.aldebaran.qi.sdk.object.actuation.Animate;
+import com.aldebaran.qi.sdk.object.actuation.Animation;
+
+public class PepperAnimationEvent extends AbstractPepperActionEvent {
+
+ public final String animationName;
+ private final IAnimationCompletedListener onLabelReachedListener;
+
+
+ /**
+ * Constructor for PepperAnimationEvent
+ *
+ * @param animationName The name of the animation to play
+ * @param onLabelReachedListener The listener to call when the animation is completed
+ */
+ public PepperAnimationEvent(String animationName, IAnimationCompletedListener onLabelReachedListener) {
+ this.animationName = animationName;
+ this.onLabelReachedListener = onLabelReachedListener;
+ }
+
+ /**
+ * Constructor for PepperAnimationEvent
+ *
+ * @param animationName The name of the animation to play
+ */
+ public PepperAnimationEvent(String animationName) {
+ this(animationName, null);
+ }
+
+ @Override
+ public EPepperAction getAction() {
+ return null;
+ }
+
+ /**
+ * Returns an animation object, which can be used to play the animation
+ * in the Pepper class.
+ */
+ public Animate getAnimation(QiContext context) {
+ Animation animation = AnimationBuilder.with(context)
+ .withResources(context.getResources().getIdentifier(animationName, "raw", context.getPackageName()))
+ .build();
+
+ Animate animate = AnimateBuilder.with(context)
+ .withAnimation(animation)
+ .build();
+
+ // Add a listener for when a label is reached
+ animate.addOnLabelReachedListener((label, time) -> {
+ if (onLabelReachedListener != null && "end".equals(label)) {
+ onLabelReachedListener.onComplete();
+ }
+ });
+ return animate;
+ }
+
+ public interface IAnimationCompletedListener {
+ void onComplete();
+ }
+}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java
new file mode 100644
index 0000000..ecf8eab
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/pepper/PepperSpeechEvent.java
@@ -0,0 +1,32 @@
+package com.example.fitbot.pepper;
+
+import com.aldebaran.qi.sdk.QiContext;
+import com.aldebaran.qi.sdk.builder.SayBuilder;
+import com.aldebaran.qi.sdk.object.conversation.Say;
+import com.aldebaran.qi.sdk.object.locale.Locale;
+
+public class PepperSpeechEvent extends AbstractPepperActionEvent {
+
+ public final String phrase;
+ public final Locale locale;
+
+ public PepperSpeechEvent(String phrase, Locale locale) {
+ this.locale = locale;
+ this.phrase = phrase;
+ }
+
+ @Override
+ public EPepperAction getAction() {
+ return EPepperAction.ACTION_SPEAK;
+ }
+
+ /**
+ * Returns a Say object, which can then be executed.
+ */
+ public Say getSay(QiContext context) {
+ return SayBuilder.with(context)
+ .withText(this.phrase)
+ .withLocale(this.locale)
+ .build();
+ }
+}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/EndScreenActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/EndScreenActivity.java
index 157bebf..f2185de 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/EndScreenActivity.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/EndScreenActivity.java
@@ -4,8 +4,7 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.example.fitbot.R;
-import com.example.fitbot.ui.activities.FitnessActivity;
-import com.example.fitbot.ui.activities.MainActivity;
+import com.example.fitbot.util.NavigationManager;
public class EndScreenActivity extends AppCompatActivity {
@@ -14,7 +13,7 @@ public class EndScreenActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
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.continueButton, FitnessActivity.class);
+ NavigationManager.setupButtonNavigation(this, R.id.homeButtonEndScreen, MainActivity.class);
+ NavigationManager.setupButtonNavigation(this, R.id.startButtonEndScreen, FitnessActivity.class);
}
}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java
index 89a857f..abde556 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java
@@ -1,7 +1,14 @@
package com.example.fitbot.ui.activities;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
import android.widget.VideoView;
import com.aldebaran.qi.sdk.QiContext;
@@ -10,25 +17,41 @@ import com.aldebaran.qi.sdk.RobotLifecycleCallbacks;
import com.aldebaran.qi.sdk.design.activity.RobotActivity;
import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayStrategy;
import com.example.fitbot.R;
-import com.example.fitbot.exercise.EMuscleGroup;
import com.example.fitbot.exercise.Exercise;
-import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
-import com.example.fitbot.util.ButtonNavigation;
-import com.example.fitbot.util.FitnessCycle;
-import com.example.fitbot.util.path.GesturePath;
+import com.example.fitbot.exercise.ExerciseManager;
+import com.example.fitbot.pepper.Pepper;
+import com.example.fitbot.ui.components.ExerciseStatusElement;
+import com.example.fitbot.util.NavigationManager;
+import com.example.fitbot.util.processing.InputProcessor;
import org.joml.Vector3f;
-import java.util.concurrent.CompletableFuture;
-
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
- PersonalMotionPreviewElement personalMotionPreviewElement;
+ // Private fields for the FitnessActivity class.
+ private ExerciseStatusElement personalMotionPreviewElement;
+ private InputProcessor motionProcessor;
+ private Exercise currentExercise;
+
+ // 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.",
+ ", 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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
QiSDK.register(this, this);
+
setContentView(R.layout.activity_fitness);
// Remove the ugly ass bar on top of the view
@@ -36,62 +59,137 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
// Find the VideoView by its ID
VideoView videoView = findViewById(R.id.videoView);
+ playVideo(videoView, this);
+ NavigationManager.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
+ NavigationManager.setupButtonNavigation(this, R.id.skipButtonFitness, MainActivity.class); //Needs to skip exercises once those are implemented
- FitnessCycle.playVideo(videoView, this);
+ NavigationManager.hideSystemUI(this);
- 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
-
- 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);
+ Button infoButtonFitness = findViewById(R.id.infoButtonFitness);
+ infoButtonFitness.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showInfoDialog();
+ }
});
}
@Override
public void onRobotFocusGained(QiContext qiContext) {
- // Find the VideoView by its ID
-// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext));
- Log.i("Motion", "qiContext provided");
- personalMotionPreviewElement.provideQiContext(qiContext);
+ // Provide the context so that all queued actions can be performed.
+ Pepper.provideContext(qiContext, this.getClass());
+
+ 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(() -> {
+ this.fetchExerciseAsync((exercise) -> {
+ // 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.initialize(exercise, motionProcessor, EXERCISE_COUNT);
+
+ motionProcessor.startListening();
+ motionProcessor.setInputHandler(personalMotionPreviewElement);
+ }, (n) -> {
+ int randomMessageIndex = (int) Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
+ Pepper.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex]);
+ Pepper.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE);
+ NavigationManager.navigateToActivity(this, EndScreenActivity.class);
+ });
+ });
// 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.
+ */
+ public void fetchExerciseAsync(Exercise.ExerciseFetchHandler onSuccessfulFetch, Exercise.ExerciseFetchHandler onFailedFetch) {
+ // For some stupid reason we cannot perform network operations on the main thread.
+ // therefore we'll have to do it like this...
+ (new Thread(() -> {
+ Exercise exercise = ExerciseManager.fetchExerciseFromDatabase();
+ if (exercise == null) {
+ onFailedFetch.handle(null);
+ } else {
+ onSuccessfulFetch.handle(exercise);
+ }
+ })).start();
+ }
+
+ /**
+ * Function for playing a video in a VideoView
+ *
+ * @param videoView The VideoView to play the video in
+ * @param context The context to use
+ */
+ public static void playVideo(VideoView videoView, Context context) {
+ // Set up the video player
+ if (videoView != null) {
+ Uri videoUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.bicepvideo);
+ videoView.setVideoURI(videoUri);
+
+ videoView.setOnCompletionListener(mp -> {
+ // Repeat the video when it finishes playing
+ videoView.start();
+ });
+
+ videoView.start();
+ } else {
+ Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
+ }
+ }
+
@Override
public void onRobotFocusLost() {
- // Implement your logic when the robot focus is lost
+ QiSDK.unregister(this, this);
+ Pepper.provideContext(null, this.getClass()); // Remove the context (unavailable)
+
+ // Go to the main screen
+ NavigationManager.navigateToActivity(this, MainActivity.class);
}
@Override
public void onRobotFocusRefused(String reason) {
- // Implement your logic when the robot focus is refused
}
@Override
protected void onDestroy() {
- super.onDestroy();
+ if (this.motionProcessor != null) {
+ this.motionProcessor.stopListening();
+ this.motionProcessor = null;
+ }
QiSDK.unregister(this, this);
- this.personalMotionPreviewElement.onDestroy();
+ Pepper.provideContext(null, this.getClass());
+
+ super.onDestroy();
+ }
+
+ private void showInfoDialog() {
+ final Dialog dialog = new Dialog(this);
+ dialog.setContentView(R.layout.dialog_info);
+
+ NavigationManager.hideSystemUI(this);
+
+ dialog.getWindow().setDimAmount(0.5f);
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
+ dialog.setCancelable(true);
+
+ Button closeButton = dialog.findViewById(R.id.closeButtonDialog);
+ closeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ }
+ });
+ dialog.show();
}
}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/HelpActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/HelpActivity.java
index 3ffa2e3..5d0a444 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/HelpActivity.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/HelpActivity.java
@@ -1,10 +1,10 @@
package com.example.fitbot.ui.activities;
-import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
import com.example.fitbot.R;
-import com.example.fitbot.util.ButtonNavigation;
+import com.example.fitbot.util.NavigationManager;
public class HelpActivity extends AppCompatActivity {
@@ -13,7 +13,8 @@ public class HelpActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
- ButtonNavigation.setupButtonNavigation(this, R.id.homeButton, MainActivity.class);
+ NavigationManager.setupButtonNavigation(this, R.id.homeButtonHelp, MainActivity.class);
+ NavigationManager.hideSystemUI(this);
}
}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/MainActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/MainActivity.java
index b2fa7ba..3aaca38 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/MainActivity.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/MainActivity.java
@@ -1,5 +1,6 @@
package com.example.fitbot.ui.activities;
+import static com.example.fitbot.util.Networking.sendIpAddress;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
@@ -10,28 +11,36 @@ import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
-import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
import android.widget.Button;
import com.example.fitbot.R;
-import com.example.fitbot.util.ButtonNavigation;
+import com.example.fitbot.util.NavigationManager;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
public class MainActivity extends AppCompatActivity {
- //Variables
+ // Variables
DrawerLayout drawerLayout;
NavigationView navigationView;
Toolbar toolbar;
Button startButton;
@SuppressLint("WrongViewCast")
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ // Set full screen mode to hide status bar
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
- Button startButton = findViewById(R.id.startButton);
+
+ startButton = findViewById(R.id.startButtonMain);
startButton.setOnClickListener(v -> {
Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bicepvideo);
Intent intent = new Intent(MainActivity.this, FitnessActivity.class);
@@ -39,7 +48,12 @@ public class MainActivity extends AppCompatActivity {
startActivity(intent);
});
- setUpUi(); // Set up the UI
+ // Set up the UI
+ setUpUi();
+
+ // Hide system UI
+ NavigationManager.hideSystemUI(this);
+ sendIpAddress(this);
}
private void setUpUi() {
@@ -47,31 +61,58 @@ public class MainActivity extends AppCompatActivity {
drawerLayout = findViewById(R.id.drawer_layout);
navigationView = findViewById(R.id.nav_view);
toolbar = findViewById(R.id.toolbar);
- startButton = findViewById(R.id.startButton);
+ startButton = findViewById(R.id.startButtonMain);
- ButtonNavigation.setupButtonNavigation(this, R.id.startButton, FitnessActivity.class);
- ButtonNavigation.setupButtonNavigation(this, R.id.helpButton, HelpActivity.class);
+ // Hide the action bar
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().hide();
+ }
+ NavigationManager.setupButtonNavigation(this, R.id.startButtonMain, FitnessActivity.class);
+ NavigationManager.setupButtonNavigation(this, R.id.helpButtonMain, HelpActivity.class);
/*---Tool 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---*/
navigationView.bringToFront(); // Make the navigation drawer menu clickable
- 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 toggle = new // Create a toggle for the navigation drawer
+ ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
+ @Override
+ public void onDrawerOpened(View drawerView) {
+ super.onDrawerOpened(drawerView);
+ }
+
+ @Override
+ public void onDrawerClosed(View drawerView) {
+ super.onDrawerClosed(drawerView);
+ }
+ };
drawerLayout.addDrawerListener(toggle);
toggle.syncState(); // Synchronize the state of the navigation drawer
}
@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();
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ NavigationManager.hideSystemUI(this);
}
}
+
+ @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();
+ }
+ }
+
+
}
+
+
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/ExerciseStatusElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/ExerciseStatusElement.java
new file mode 100644
index 0000000..606321b
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/ExerciseStatusElement.java
@@ -0,0 +1,227 @@
+package com.example.fitbot.ui.components;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.example.fitbot.exercise.Exercise;
+import com.example.fitbot.pepper.Pepper;
+import com.example.fitbot.ui.activities.EndScreenActivity;
+import com.example.fitbot.ui.activities.FitnessActivity;
+import com.example.fitbot.ui.activities.MainActivity;
+import com.example.fitbot.util.NavigationManager;
+import com.example.fitbot.util.processing.IInputHandler;
+import com.example.fitbot.util.processing.InputProcessor;
+
+import org.joml.Matrix4f;
+import org.joml.Vector2f;
+import org.joml.Vector3f;
+import org.joml.Vector4f;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class ExerciseStatusElement extends View implements IInputHandler {
+
+ // Fields regarding Exercise and speech handling.
+ private InputProcessor motionProcessor;
+ private Exercise exercise;
+ private int exerciseCount;
+
+ private FitnessActivity parentActivity;
+
+ private final Paint userProgressPaint = new Paint();
+ private final Paint borderPaint = new Paint();
+ private final Paint backgroundPaint = new Paint();
+
+ // TODO: Remove
+ private final Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation.
+ private final Matrix4f projectionMatrix = new Matrix4f(); // The projection matrix for the 3D to 2D transformation.
+ private final Vector4f objectPosition = new Vector4f(0, 0, 0, 1); // The location of the object in 3D space.
+ private final ConcurrentLinkedQueue vectors = new ConcurrentLinkedQueue<>();
+
+ // TODO: Remove
+ private Vector2f[] axisVectors = new Vector2f[0];
+
+
+ private static final String[] STARTING_PHRASES = {
+ "Veel success met de oefening!",
+ "Je kan het!",
+ "Veel plezier!"
+ };
+
+
+ public ExerciseStatusElement(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ if (context instanceof Activity) {
+ this.parentActivity = (FitnessActivity) context;
+ }
+
+ 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.
+ this.borderPaint.setColor(-1);
+ this.borderPaint.setStyle(Paint.Style.STROKE);
+ this.borderPaint.setStrokeWidth(5.0f);
+ this.borderPaint.setAntiAlias(true);
+
+ this.backgroundPaint.setColor(0xFF000000); // Black
+ }
+
+ /**
+ * Method for initializing the PersonalMotionPreviewElement.
+ * This method has to be called with a "post" function when the element has been
+ * created, otherwise the dimensions of the element aren't initialized yet, which
+ * will cause the vertex projections to fail (0 width and height).
+ *
+ * @param exercise The exercise that the user is currently performing.
+ * @param 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(@Nullable Exercise exercise, InputProcessor motionProcessor, int exerciseCount) {
+ Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
+
+ this.motionProcessor = motionProcessor;
+ this.exercise = exercise;
+ this.exerciseCount = exerciseCount;
+
+ /* TODO: Remove */
+ this.axisVectors = new Vector2f[]{
+ projectVertex(new Vector3f(-5.0f, 0, 0), getWidth(), getHeight()),
+ projectVertex(new Vector3f(5.0f, 0, 0), getWidth(), getHeight()),
+ projectVertex(new Vector3f(0, -5.0f, 0), getWidth(), getHeight()),
+ projectVertex(new Vector3f(0, 5.0f, 0), getWidth(), getHeight()),
+ projectVertex(new Vector3f(0, 0, -5.0f), getWidth(), getHeight()),
+ projectVertex(new Vector3f(0, 0, 5.0f), getWidth(), getHeight())
+ };
+
+ Pepper.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]);
+
+ // Handler that is called every time the motion processor receives new data.
+ this.motionProcessor.setInputHandler((rotationVector, deviceId) -> {
+
+ Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
+ Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed()));
+
+ if (this.motionProcessor.hasFinished()) {
+ // If for some reason the parent activity is not defined,
+ // move back to the main screen.
+ if (this.parentActivity == null) {
+ // Move to main screen
+ NavigationManager.navigateToActivity(getContext(), MainActivity.class);
+ return;
+ }
+ // Move on to the next exercise, or finish.
+ if (this.exerciseCount > 0) {
+ this.exerciseCount--;
+ this.parentActivity.fetchExerciseAsync((newExercise) -> {
+ this.motionProcessor.useExercise(newExercise);
+ }, (failed) -> {
+ // Move to main screen
+ NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
+ });
+ } else {
+ // Finish the exercise.
+ NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
+ return;
+ }
+ }
+
+ /* TODO: Use raw vector */
+ vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight()));
+
+ /* TODO: Remove */
+ Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight());
+
+ this.vectors.add(parsed /* TODO: Add regular vertex once exercise data is stored in DB*/);
+
+ // Remove the first element if the array is too big
+ if (this.vectors.size() > 100)
+ this.vectors.poll();
+ });
+ }
+
+ /**
+ * Method for setting the gesture path that will be drawn on the canvas.
+ *
+ * @param exercise The exercise that the user is currently performing.
+ */
+ public void setExercise(Exercise exercise) {
+ this.motionProcessor.useExercise(exercise);
+ this.exercise = exercise;
+ }
+
+
+ private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) {
+ viewMatrix
+ .identity()
+ .lookAt(new Vector3f(0, 0, -2), new Vector3f(0, 0, 0), new Vector3f(0, 1, 0));
+
+ // Transform the projection matrix to a perspective projection matrix
+ // Perspective transformation conserves the depth of the object
+ projectionMatrix
+ .identity()
+ .perspective((float) Math.toRadians(70), (float) virtualWidth / virtualHeight, .01f, 1000.0f);
+
+ // Convert world coordinates to screen-space using MVP matrix
+ Vector4f screenCoordinates = new Vector4f(point, 1.0f)
+ .mul(this.viewMatrix)
+ .mul(this.projectionMatrix);
+
+ // Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight)
+ float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth;
+ float normalizedY = (1.0f - screenCoordinates.y / screenCoordinates.w) * 0.5f * virtualHeight;
+ return new Vector2f(normalizedX, normalizedY);
+ }
+
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
+ this.setBackgroundColor(0xFF000000); // Black
+ /*if (this.exercise == null)
+ return;*/
+
+
+ /* TODO: Remove */
+ for (int i = 0, startX, endX, startY, endY; i < axisVectors.length / 2; i++) {
+ startX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2].x));
+ endX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2 + 1].x));
+ startY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2].y));
+ endY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2 + 1].y));
+ canvas.drawLine(startX, startY, endX, endY, this.borderPaint);
+ }
+
+ for (Vector2f point : this.vectors) {
+ canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint);
+ }
+/*
+ // Draw target circle
+ float targetRadius = (this.screenDimensions.x + this.screenDimensions.y) / 5.0f;
+ canvas.drawCircle(this.screenDimensions.x / 2, this.screenDimensions.y / 2, targetRadius, this.targetPaint);
+ canvas.drawCircle(this.screenDimensions.x / 2, this.screenDimensions.y / 2, (targetRadius * exerciseProgress.get()/1000.0f), this.referencePaint);
+ referencePaint.setColor(
+ Color.argb(
+ 255,
+ (int)(255 * (1.0 - exerciseProgress.get()/1000.0f)),
+ (int)(255 * exerciseProgress.get()/1000.0f),
+ 0
+ )
+ );*/
+
+ this.invalidate();
+ }
+
+ @Override
+ public void accept(Vector3f rotationVector, int sensorId) {
+
+ }
+}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java
deleted file mode 100644
index 61e13a0..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java
+++ /dev/null
@@ -1,178 +0,0 @@
-package com.example.fitbot.ui.components;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.aldebaran.qi.sdk.QiContext;
-import com.example.fitbot.exercise.Exercise;
-import com.example.fitbot.util.FitnessCycle;
-import com.example.fitbot.util.path.GesturePath;
-import com.example.fitbot.util.path.PathSegment;
-import com.example.fitbot.util.processing.MotionData;
-import com.example.fitbot.util.processing.MotionProcessor;
-
-import org.joml.Matrix4f;
-import org.joml.Vector2f;
-import org.joml.Vector3f;
-import org.joml.Vector4f;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class PersonalMotionPreviewElement extends View {
-
- 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;
-
- private Exercise exercise;
-
- private Path targetPath; // The path the user is supposed to follow.
- private Path actualPath; // The path the user is currently following.
-
- private final Paint referencePaint = new Paint();
- private final Paint targetPaint = new Paint();
- private final Paint backgroundColor = new Paint();
-
-
- private static final String[] USER_PHRASES = {
- "Veel success met de oefening!",
- "Je kan het!",
- "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) {
- super(context, attrs);
-
- this.referencePaint.setColor(0xFFFF0000); // Red
- this.referencePaint.setStyle(Paint.Style.FILL);
- this.referencePaint.setStrokeWidth(5.0f);
- this.referencePaint.setAntiAlias(true);
-
- // Target paint is the filling of the target path.
- this.targetPaint.setColor(-1);
- this.targetPaint.setStyle(Paint.Style.STROKE);
- this.targetPaint.setStrokeWidth(5.0f);
- this.targetPaint.setAntiAlias(true);
- }
-
- /**
- * Method for initializing the PersonalMotionPreviewElement.
- * This method has to be called with a "post" function when the element has been
- * created, otherwise the dimensions of the element aren't initialized yet, which
- * will cause the vertex projections to fail (0 width and height).
- *
- * @param exercise The exercise that the user is currently performing.
- */
- public void initialize(Exercise exercise) {
- 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.exercise = exercise;
- this.paths = exercise.getPath();
- }
-
- public void onDestroy()
- {
- if ( this.motionProcessor != null )
- this.motionProcessor.stopListening();
-
- this.motionProcessor = null;
- }
-
- /**
- * Function for providing a QiContext to the PersonalMotionPreviewElement.
- * This function will be called by the parent activity when the QiContext is available.
- * Also say something nice to the user :)
- *
- * @param context The QiContext to provide.
- */
- public void provideQiContext(QiContext context) {
- try {
- 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.
- this.motionProcessor.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate, deviceId) -> {
- int progress = (int) this.motionProcessor.getError(this.paths[0], processed);
- this.exerciseProgress.set(Math.min(1000, Math.max(0, progress)));
- Log.i("MotionProcessor", "Processed data: " + progress + " (" + preprocessed + ")");
- });
- saySomethingNice();
- } catch (Exception e) {
- Log.e("MotionProcessor", "An error occurred whilst attempting to provide QiContext:" + e.getMessage());
- }
- }
-
- /**
- * Function to say something nice to the user :)
- */
- private void saySomethingNice()
- {
- if (this.qiContext == null)
- return;
-
- FitnessCycle.say(USER_PHRASES[(int) Math.floor(Math.random() * USER_PHRASES.length)], this.qiContext);
- }
-
- /**
- * Method for setting the gesture path that will be drawn on the canvas.
- *
- * @param exercise The exercise that the user is currently performing.
- */
- public void setExercise(Exercise exercise) {
- this.exercise = exercise;
- }
-
-
- @Override
- public void onDraw(Canvas canvas) {
- canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor);
- this.setBackgroundColor(0xFF000000); // Black
- if (this.exercise == null)
- return;
-
- // Draw target circle
- float targetRadius = (this.screenDimensions.x + this.screenDimensions.y) / 5.0f;
- canvas.drawCircle(this.screenDimensions.x / 2, this.screenDimensions.y / 2, targetRadius, this.targetPaint);
- canvas.drawCircle(this.screenDimensions.x / 2, this.screenDimensions.y / 2, (targetRadius * exerciseProgress.get()/1000.0f), this.referencePaint);
- referencePaint.setColor(
- Color.argb(
- 255,
- (int)(255 * (1.0 - exerciseProgress.get()/1000.0f)),
- (int)(255 * exerciseProgress.get()/1000.0f),
- 0
- )
- );
-
- this.invalidate();
-
- timePassed = (System.nanoTime() - startingTime) / 1E9D;
- }
-}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Animations.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Animations.java
index 01444f4..6d243c1 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Animations.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Animations.java
@@ -11,8 +11,7 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
public class Animations {
- public static void Animate(String AnimationFile, QiContext ctx)
- {
+ public static void Animate(String AnimationFile, QiContext ctx) {
int resId = ctx.getResources().getIdentifier(AnimationFile, "raw", ctx.getPackageName());
Animation animation = AnimationBuilder.with(ctx)
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/ButtonNavigation.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/ButtonNavigation.java
deleted file mode 100644
index b239b35..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/ButtonNavigation.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.example.fitbot.util;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.view.View;
-import android.widget.Button;
-
-public class ButtonNavigation {
-
- /**
- * Sets up a button to navigate to a different activity when clicked.
- *
- * @param currentActivity The activity that contains the button
- * @param buttonId The ID of the button
- * @param targetActivity The activity to navigate to
- */
- public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class extends Activity> targetActivity) {
- Button button = currentActivity.findViewById(buttonId);
- button.setOnClickListener(v -> {
- Intent intent = new Intent(currentActivity, targetActivity);
- currentActivity.startActivity(intent);
- currentActivity.finish();
- });
- }
-}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java
deleted file mode 100644
index a55b445..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/FitnessCycle.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.example.fitbot.util;
-
-import android.content.Context;
-import android.net.Uri;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.widget.VideoView;
-
-import com.aldebaran.qi.sdk.builder.SayBuilder;
-import com.aldebaran.qi.sdk.object.locale.Language;
-import com.aldebaran.qi.sdk.object.locale.Locale;
-import com.aldebaran.qi.sdk.object.locale.Region;
-import com.example.fitbot.R;
-import com.aldebaran.qi.sdk.QiContext;
-import com.aldebaran.qi.sdk.builder.AnimateBuilder;
-import com.aldebaran.qi.sdk.builder.AnimationBuilder;
-import com.aldebaran.qi.sdk.object.actuation.Animate;
-import com.aldebaran.qi.sdk.object.actuation.Animation;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class FitnessCycle extends AppCompatActivity {
-
- private static final Locale DUTCH_LOCALE = new Locale(Language.DUTCH, Region.NETHERLANDS);
-
-
- /**
- * Function for executing a movement animation a certain number of times
- * on the robot
- *
- * @param Exercise The name of the exercise to perform
- * @param Reps The number of repetitions to perform
- * @param qiContext The QiContext to use
- */
- public static void executeMovement(String Exercise, int Reps, QiContext qiContext) {
- AtomicInteger repCount = new AtomicInteger(0);
-
- Animation animation = AnimationBuilder.with(qiContext)
- .withResources(qiContext.getResources().getIdentifier(Exercise, "raw", qiContext.getPackageName()))
- .build();
-
- Animate animate = AnimateBuilder.with(qiContext)
- .withAnimation(animation)
- .build();
-
- // Add a listener for when a label is reached
- animate.addOnLabelReachedListener((label, time) -> {
- // Increment repCount when the end of a repetition is reached
- if ("end".equals(label)) {
- repCount.incrementAndGet();
- }
- });
-
- // Run the animation the desired number of times
- for (int i = 0; i < Reps; i++) {
- animate.run();
- }
- }
-
- /**
- * Function for making the robot say something with DUTCH_LOCALE as locale
- * @param phrase The phrase to make the robot say
- * @param ctx The QiContext to use
- */
- public static void say(String phrase, QiContext ctx)
- {
- say(phrase, ctx, DUTCH_LOCALE);
- }
-
- /**
- * Function for making the robot say something with a specific locale
- * @param phrase The phrase to make the robot say
- * @param ctx The QiContext to use
- * @param locale The locale to use
- */
- public static void say(String phrase, QiContext ctx, Locale locale)
- {
- SayBuilder
- .with(ctx)
- .withLocale(locale)
- .withText(phrase)
- .build()
- .run();
- }
-
- /**
- * Function for playing a video in a VideoView
- *
- * @param videoView The VideoView to play the video in
- * @param context The context to use
- */
- public static void playVideo(VideoView videoView, Context context) {
- // Set up the video player
- if (videoView != null) {
- Uri videoUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.bicepvideo);
- videoView.setVideoURI(videoUri);
-
- videoView.setOnCompletionListener(mp -> {
- // Repeat the video when it finishes playing
- videoView.start();
- });
-
- videoView.start();
- } else {
- Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
- }
- }
-}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java
new file mode 100644
index 0000000..a906b53
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java
@@ -0,0 +1,60 @@
+package com.example.fitbot.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+import android.widget.Button;
+
+public class NavigationManager {
+
+ /**
+ * Sets up a button to navigate to a different activity when clicked.
+ *
+ * @param currentActivity The activity that contains the button
+ * @param buttonId The ID of the button
+ * @param targetActivity The activity to navigate to
+ */
+ public static void setupButtonNavigation(Activity currentActivity, int buttonId, Class extends Activity> targetActivity) {
+ Button button = currentActivity.findViewById(buttonId);
+ if (button == null) {
+ throw new IllegalArgumentException("Button with ID " + buttonId + " not found in " + currentActivity.getClass().getSimpleName());
+ }
+ button.setOnClickListener(v -> NavigationManager.navigateToActivity(currentActivity, targetActivity));
+ }
+
+ /**
+ * Navigates to the target activity.
+ *
+ * @param currentActivity The current activity
+ * @param targetActivity The target activity
+ */
+ public static void navigateToActivity(Activity currentActivity, Class extends Activity> targetActivity) {
+ Intent intent = new Intent(currentActivity, targetActivity);
+ // Close previous activity
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ currentActivity.startActivity(intent);
+ currentActivity.finish();
+ }
+
+ /**
+ * Navigates to the target activity.
+ *
+ * @param context The context
+ * @param targetActivity The target activity
+ */
+ public static void navigateToActivity(Context context, Class extends Activity> targetActivity) {
+ Intent intent = new Intent(context, targetActivity);
+ context.startActivity(intent);
+ }
+
+ public static void hideSystemUI(Activity currentActivity) {
+ View decorView = currentActivity.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);
+ }
+
+}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Networking.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Networking.java
new file mode 100644
index 0000000..db0e6f2
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/Networking.java
@@ -0,0 +1,76 @@
+package com.example.fitbot.util;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+public class Networking {
+
+ public static void sendIpAddress(final Context context) {
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ String ipAddress = getIPAddress(context);
+ String jsonInputString = "{\"ip\":\"" +
+ ipAddress +
+ "\"}";
+
+ byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
+ HttpURLConnection conn = null;
+ try {
+ URL url = new URL("http://145.92.8.132:443/set-ip"); // Replace with your Node server URL
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Type", "application/json");
+ conn.setDoOutput(true);
+
+ OutputStream os = conn.getOutputStream();
+ os.write(input, 0, input.length);
+ os.close();
+
+ int responseCode = conn.getResponseCode();
+ Log.i("NetworkUtils", "Response Code: " + responseCode);
+ } catch (Exception e) {
+ Log.e("NetworkUtils", "Error sending IP address", e);
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ return null;
+ }
+ }.execute();
+ }
+
+
+ private static String getIPAddress(Context context) {
+ WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
+
+ // Convert little-endian to big-endian if needed
+ if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
+ ipAddress = Integer.reverseBytes(ipAddress);
+ }
+
+ byte[] ipByteArray = BigInteger.valueOf(ipAddress).toByteArray();
+
+ String ip = "";
+ try {
+ ip = InetAddress.getByAddress(ipByteArray).getHostAddress();
+ } catch (UnknownHostException ex) {
+ Log.e("WIFIIP", "Unable to get host address.");
+ }
+
+ return ip;
+ }
+}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java
index 06ebe10..14e7706 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java
@@ -2,10 +2,6 @@ package com.example.fitbot.util.path;
import org.joml.Vector3f;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
public class GesturePath {
// The vectors that make up the path.
@@ -16,18 +12,18 @@ public class GesturePath {
*
* @param vectors The vectors that make up the path.
*/
- public GesturePath(Vector3f[] vectors)
- {
- if ( vectors.length < 2)
+ public GesturePath(Vector3f[] vectors) {
+ if (vectors.length < 2)
throw new IllegalArgumentException("A path must have at least two points.");
this.segments = new PathSegment[vectors.length - 1];
- for ( int i = 0; i < vectors.length - 1; i++)
+ for (int i = 0; i < vectors.length - 1; i++)
segments[i] = new PathSegment(vectors[i], vectors[i + 1]);
}
/**
* Constructor for a GesturePath with provided PathSegments.
+ *
* @param segments The PathSegments to initialize the path with.
*/
public GesturePath(PathSegment... segments) {
@@ -43,6 +39,18 @@ public class GesturePath {
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.
*
@@ -51,11 +59,11 @@ public class GesturePath {
*/
public PathSegment closest(Vector3f reference) {
// If there's only one segment, return that one.
- if ( segments.length == 1)
+ if (segments.length == 1)
return segments[0];
PathSegment closest = segments[0];
- for ( int i = 1; i < segments.length; i++)
+ for (int i = 1; i < segments.length; i++)
closest = PathSegment.closer(closest, segments[i], reference);
return closest;
@@ -71,48 +79,40 @@ public class GesturePath {
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 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
+ */
- /**
- * Constructor for the Builder object.
- *
- * @param vectors The list of vectors to add.
- */
- public Builder(List vectors) {
- this.vectors = vectors;
+ public static GesturePath fromString(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");
}
+ Vector3f[] vectors = new Vector3f[input.length() / 12];
- /**
- * Default constructor for the Builder object.
- */
- public Builder() {
- this.vectors = new ArrayList<>();
+ 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)
+ );
+ }
+ vectors[i / 12] = new Vector3f(xyz[0], xyz[1], xyz[2]);
}
-
- /**
- * 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]));
- }
-
+ return new GesturePath(vectors);
}
-
}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/PathSegment.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/PathSegment.java
index 11183ca..4fc9984 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/PathSegment.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/PathSegment.java
@@ -12,7 +12,7 @@ public class PathSegment {
* pointing straight upwards relative to the line, with a curvature of 0.0.
*
* @param start The starting point of the line segment
- * @param end The end point of the line segment.
+ * @param end The end point of the line segment.
*/
public PathSegment(Vector3f start, Vector3f end) {
this.start = start;
@@ -25,7 +25,7 @@ public class PathSegment {
* depending on the curvature of the curve. If the curvature is unset, or set to 0
* then this method will use linear interpolation. Otherwise, it will use a curve.
*
- * @param t The interpolation value between 0 and 1.
+ * @param t The interpolation value between 0 and 1.
*/
public Vector3f interpolate(double t) {
return new Vector3f(this.start)
@@ -56,7 +56,7 @@ public class PathSegment {
(float) (this.start.x + t * (this.end.x - this.start.x)),
(float) (this.start.y + t * (this.end.y - this.start.y)),
(float) (this.start.z + t * (this.end.z - this.start.z))
- ));
+ ));
}
/**
@@ -84,7 +84,7 @@ public class PathSegment {
* @return The distance to the closest point on the path segment.
*/
public double distance(Vector3f reference) {
- if ( this.start.distanceSquared(reference) > this.end.distanceSquared(reference))
+ if (this.start.distanceSquared(reference) > this.end.distanceSquared(reference))
return this.end.distance(reference);
return this.start.distance(reference);
}
@@ -92,8 +92,8 @@ public class PathSegment {
/**
* Function for returning the closest path segment to a reference point.
*
- * @param first The first path segment to compare.
- * @param second The second path segment to compare.
+ * @param first The first path segment to compare.
+ * @param second The second path segment to compare.
* @param referencePoint The reference point to compare to.
* @return The closest path segment to the reference point.
*/
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IInputHandler.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IInputHandler.java
new file mode 100644
index 0000000..9d265fd
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IInputHandler.java
@@ -0,0 +1,15 @@
+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);
+
+}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java
deleted file mode 100644
index dd3220c..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java
+++ /dev/null
@@ -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);
-
-}
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java
new file mode 100644
index 0000000..c78bd89
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java
@@ -0,0 +1,337 @@
+package com.example.fitbot.util.processing;
+
+import android.util.Log;
+
+import com.example.fitbot.exercise.Exercise;
+import com.example.fitbot.exercise.ExerciseManager;
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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 exerciseDurationInSeconds;
+
+ /**
+ * This field is used to determine if the motion data is being recorded.
+ * If this is the case, instead of functioning normally, the element
+ * will record the movement that the user makes, store it in the
+ * `selfRotationVectorPaths` field and send it to the server.
+ */
+ private boolean recordingMovement = false;
+
+ /**
+ * Represents the duration of the recording in seconds.
+ * This field only has effect when the `recordingMovement` field is set to true.
+ */
+ private float recordingDurationInSeconds = 0.0f;
+
+ // 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;
+
+ private static final String[] REQUIRED_SENSOR_JSON_PROPERTIES =
+ {"rotationX", "rotationY", "rotationZ", "type", "deviceId"};
+
+ // The web server that listens for incoming motion data.
+ 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.exerciseDurationInSeconds = 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) {
+ if ( this.recordingMovement )
+ throw new IllegalStateException("Cannot change exercise while recording movement.");
+
+ this.selfRotationVectorPaths = new Vector3f[2][(int) (exercise.exerciseTimeInSeconds * this.sampleRate)];
+ this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getVectors().length];
+ this.exerciseDurationInSeconds = exercise.exerciseTimeInSeconds;
+ this.secondsPassed = 0.0D;
+ this.lastTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Function for setting whether the motion data
+ * should be recorded or not.
+ *
+ * @param recording Whether the motion data should be recorded.
+ * @param duration For how long the motion data should be recorded.
+ * This only has an effect if `recording` is true.
+ */
+ public void setRecording(boolean recording, float duration) {
+ this.recordingMovement = recording;
+ this.recordingDurationInSeconds = duration;
+ if (recording) {
+ this.secondsPassed = 0.0D;
+ this.lastTime = System.currentTimeMillis();
+
+ }
+ }
+
+ /**
+ * Function for checking if the exercise or recording has finished.
+ * This function will return true if the execution of the exercise has finished or
+ * if the recording has finished, depending on the state of the `recordingMovement` field.
+ *
+ * @return Whether the exercise or recording has finished.
+ */
+ public boolean hasFinished() {
+ return this.recordingMovement ?
+ (this.secondsPassed >= this.recordingDurationInSeconds) :
+ (this.secondsPassed >= this.exerciseDurationInSeconds);
+ }
+
+ /**
+ * 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();
+
+ // Ensure all properties are present in the received JSON object
+ for (String s : REQUIRED_SENSOR_JSON_PROPERTIES) {
+ 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;
+
+ if ( this.recordingMovement && this.hasFinished()) {
+ // Do something with the recorded data.
+ this.recordingMovement = false;
+ // Convert recorded data from `selfRotationVectorPaths` to string, and
+ // publish to database, or do something else with it.
+
+ String converted = convertRecordedDataToString();
+
+ // Do something with it
+ Log.i("MotionProcessor", "Converted data: ");
+ Log.i("MotionProcessor", converted);
+ }
+ }
+ }
+
+ /**
+ * Function for converting the recorded data to a string.
+ * This function will convert the recorded data to a string
+ * that can be sent to a database or other storage.
+ *
+ * @return The converted string.
+ */
+ private String convertRecordedDataToString()
+ {
+ // First, remove empty entries
+ StringBuilder pathBuilder = new StringBuilder();
+
+ int[] intBits = new int[3];
+ char[] vectorChars = new char[12]; // 4 bytes per scalar, 12 chars per vector
+
+ // Iterate over all devices. In the current instance, it's 2.
+ for ( int deviceId = 0; deviceId < selfRotationVectorPaths.length; deviceId++) {
+ for (Vector3f dataPoint : selfRotationVectorPaths[deviceId]) {
+ if (dataPoint != null) {
+ // Convert float to int bits for conversion to char
+ intBits[0] = Float.floatToIntBits(dataPoint.x);
+ intBits[1] = Float.floatToIntBits(dataPoint.y);
+ intBits[2] = Float.floatToIntBits(dataPoint.z);
+
+ // Convert int bits to char, in Big Endian order.
+ // This is important for converting back to float later.
+ for (int i = 0; i < 3; i++) {
+ vectorChars[i * 4] = (char) (intBits[i] >> 24);
+ vectorChars[i * 4 + 1] = (char) (intBits[i] >> 16);
+ vectorChars[i * 4 + 2] = (char) (intBits[i] >> 8);
+ vectorChars[i * 4 + 3] = (char) intBits[i];
+ }
+
+ pathBuilder.append(vectorChars);
+ }
+ }
+ // Add a separator between devices
+ if ( deviceId < selfRotationVectorPaths.length - 1)
+ pathBuilder.append(ExerciseManager.PATH_DELIMITER);
+ }
+ return pathBuilder.toString();
+ }
+
+ /**
+ * 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 / exerciseDurationInSeconds;
+ }
+
+ /**
+ * 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.exerciseDurationInSeconds / 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 &&
+ this.selfRotationVectorPaths[sensorId][selfIndex] != null &&
+ this.targetRotationVectorPaths[sensorId][targetIndex] != null
+ ) {
+ 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.exerciseDurationInSeconds; i++) {
+ error += getError(sensorId, i);
+ }
+ return error / this.exerciseDurationInSeconds;
+ }
+
+ public float secondsPassed() {
+ return (float) secondsPassed;
+ }
+}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java
deleted file mode 100644
index fae42fd..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java
+++ /dev/null
@@ -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])
- );
- }
-}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java
deleted file mode 100644
index 875f731..0000000
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java
+++ /dev/null
@@ -1,228 +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.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 relativeLeftPath = new ArrayList<>(); // Relative path of the left motion data
- private final List relativeRightPath = new ArrayList<>(); // Relative path of the motion data
-
- private Vector3f ZERO = new Vector3f(0, 0, 0);
-
- private final float sampleRate = 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) {
- // Don't do anything ... just ignore the exception
- 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 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 relativeLeftPath, List 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
- return motionData.acceleration
- .rotateX(-motionData.rotation.x)
- .rotateY(-motionData.rotation.y)
- .rotateZ(-motionData.rotation.z)
- .div(2)
- .mul(sampleRate * sampleRate);
- }
-
- /**
- * 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 getErrors(GesturePath referencePath) {
-
- List 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());
- }
-}
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebServerHandler.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebServerHandler.java
index 98739b4..67327cb 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebServerHandler.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebServerHandler.java
@@ -1,7 +1,5 @@
package com.example.fitbot.util.server;
-import java.net.Socket;
-
/**
* Interface for handling WebSocket events.
*/
diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebServer.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebServer.java
index 7fe001a..87dc331 100644
--- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebServer.java
+++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebServer.java
@@ -10,19 +10,16 @@ import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
public class WebServer implements Runnable {
private ServerSocket serverSocket;
- protected IWebServerHandler eventHandler = (input) -> {}; // No-op.
+ protected IWebServerHandler eventHandler = (input) -> {
+ }; // No-op.
private Thread thread;
- private AtomicBoolean forceClose = new AtomicBoolean(false);
+ private final AtomicBoolean forceClose = new AtomicBoolean(false);
/**
* Constructor for creating a new WebSocket server.
@@ -84,7 +81,7 @@ public class WebServer implements Runnable {
String[] data = builder.toString().split("\n\n");
- if ( data.length > 1) { // Check if the data is valid.
+ if (data.length > 1) { // Check if the data is valid.
this.eventHandler.onReceive(data[1]);
}
diff --git a/code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml b/code/src/Fitbot/app/src/main/res/drawable/big_red_button_gradient.xml
similarity index 63%
rename from code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml
rename to code/src/Fitbot/app/src/main/res/drawable/big_red_button_gradient.xml
index 0ade340..878f41e 100644
--- a/code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml
+++ b/code/src/Fitbot/app/src/main/res/drawable/big_red_button_gradient.xml
@@ -2,11 +2,12 @@
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/border_background.xml b/code/src/Fitbot/app/src/main/res/drawable/border_background.xml
new file mode 100644
index 0000000..7ea69e6
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/border_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/border_background_2.xml b/code/src/Fitbot/app/src/main/res/drawable/border_background_2.xml
new file mode 100644
index 0000000..0fb955b
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/border_background_2.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/border_background_3.xml b/code/src/Fitbot/app/src/main/res/drawable/border_background_3.xml
new file mode 100644
index 0000000..4543b55
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/border_background_3.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/box_background.xml b/code/src/Fitbot/app/src/main/res/drawable/box_background.xml
new file mode 100644
index 0000000..53f8ad7
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/box_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/fitbot_launcher_background.xml b/code/src/Fitbot/app/src/main/res/drawable/fitbot_launcher_background.xml
new file mode 100644
index 0000000..ca3826a
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/fitbot_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_check_48.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_check_48.xml
new file mode 100644
index 0000000..c4f67b4
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_check_48.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_close_48.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_close_48.xml
new file mode 100644
index 0000000..72f8573
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_close_48.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_24.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_24.xml
new file mode 100644
index 0000000..17255b7
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_40.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_40.xml
new file mode 100644
index 0000000..df12462
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_40.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_48.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_48.xml
new file mode 100644
index 0000000..15875d1
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_info_48.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_skip_next_48.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_skip_next_48.xml
new file mode 100644
index 0000000..400f0a5
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_skip_next_48.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml b/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml
index 3050a42..3a8cd35 100644
--- a/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml
@@ -1,170 +1,74 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/drawable/rectangle.xml b/code/src/Fitbot/app/src/main/res/drawable/rectangle.xml
deleted file mode 100644
index 20ab824..0000000
--- a/code/src/Fitbot/app/src/main/res/drawable/rectangle.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml b/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml
index 7190f99..a087a43 100644
--- a/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml
+++ b/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml
@@ -9,4 +9,5 @@
android:startColor="#990000"
android:endColor="#FF0000"
android:angle="90"/>
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml b/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml
index b07bcad..35aa21a 100644
--- a/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml
@@ -1,77 +1,113 @@
+ android:background="@color/darkBlue"
+ tools:context=".ui.activities.HelpActivity">
-
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:id="@+id/homeButtonEndScreen"
+ android:layout_width="150dp"
+ android:layout_height="75dp"
+ android:layout_marginEnd="280dp"
+ android:layout_marginBottom="30dp"
+ android:background="@drawable/red_button_gradient"
+ android:drawableTop="@drawable/ic_baseline_home_48"
+ android:drawableTint="@color/white"
+ android:padding="15dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+ app:layout_constraintStart_toStartOf="parent" />
-
-
-
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml b/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml
index 1ddca4c..d72d83c 100644
--- a/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml
@@ -1,83 +1,170 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/darkBlue"
+ android:fitsSystemWindows="true"
+ tools:context=".ui.activities.FitnessActivity"
+ tools:openDrawer="start">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tools:ignore="SpeakableTextPresentCheck" />
-
-
-
-
-
-
-
+ app:layout_constraintTop_toTopOf="parent" />
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/activity_help.xml b/code/src/Fitbot/app/src/main/res/layout/activity_help.xml
index 21031a2..f229ae5 100644
--- a/code/src/Fitbot/app/src/main/res/layout/activity_help.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/activity_help.xml
@@ -4,61 +4,98 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/black"
+ android:background="@color/darkBlue"
+ android:fitsSystemWindows="true"
tools:context=".ui.activities.HelpActivity">
-
-
+ app:layout_constraintStart_toStartOf="parent" />
-
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/activity_main.xml b/code/src/Fitbot/app/src/main/res/layout/activity_main.xml
index 49bfb75..0a7c0e1 100644
--- a/code/src/Fitbot/app/src/main/res/layout/activity_main.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/activity_main.xml
@@ -4,9 +4,7 @@
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#232323"
android:fitsSystemWindows="true"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
tools:context=".ui.activities.MainActivity"
tools:openDrawer="start">
@@ -26,66 +24,95 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
-
+ app:layout_constraintTop_toTopOf="parent">
-
+
+
+
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.727" />
-
-
+
+
@@ -96,7 +123,11 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
+ android:background="@color/darkBlue"
app:headerLayout="@layout/header"
+ app:itemIconTint="@color/white"
+ app:itemTextColor="@color/white"
+
app:menu="@menu/main_menu" />
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/dialog_info.xml b/code/src/Fitbot/app/src/main/res/layout/dialog_info.xml
new file mode 100644
index 0000000..f64e32a
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/layout/dialog_info.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/layout/header.xml b/code/src/Fitbot/app/src/main/res/layout/header.xml
index 0890ad8..82ee588 100644
--- a/code/src/Fitbot/app/src/main/res/layout/header.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/header.xml
@@ -24,7 +24,7 @@
android:layout_marginTop="60dp"
android:text="FitBot"
android:textSize="48sp"
- android:textColor="@color/black"
+ android:textColor="@color/darkBlue"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/code/src/Fitbot/app/src/main/res/layout/toolbar.xml b/code/src/Fitbot/app/src/main/res/layout/toolbar.xml
index 0e88a5a..e4826f9 100644
--- a/code/src/Fitbot/app/src/main/res/layout/toolbar.xml
+++ b/code/src/Fitbot/app/src/main/res/layout/toolbar.xml
@@ -3,6 +3,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
- android:elevation="8dp">
-
+ android:elevation="8dp"
+ android:theme="@style/ToolbarNav">
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/menu/main_menu.xml b/code/src/Fitbot/app/src/main/res/menu/main_menu.xml
index 38ae31c..19e5f2d 100644
--- a/code/src/Fitbot/app/src/main/res/menu/main_menu.xml
+++ b/code/src/Fitbot/app/src/main/res/menu/main_menu.xml
@@ -12,20 +12,14 @@
-
-
-
-
-
+
+
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher.xml b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher.xml
new file mode 100644
index 0000000..99a3772
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher_round.xml b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher_round.xml
new file mode 100644
index 0000000..99a3772
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index eca70cf..cad2118 100644
--- a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index eca70cf..cad2118 100644
--- a/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher.png b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher.png
new file mode 100644
index 0000000..3093063
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_foreground.png b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_foreground.png
new file mode 100644
index 0000000..5d57314
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_foreground.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_round.png b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_round.png
new file mode 100644
index 0000000..8b44e8a
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher_round.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher.png b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher.png
new file mode 100644
index 0000000..f54e67c
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_foreground.png b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_foreground.png
new file mode 100644
index 0000000..a82b6b7
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_foreground.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_round.png b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_round.png
new file mode 100644
index 0000000..547ad86
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher_round.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher.png b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher.png
new file mode 100644
index 0000000..0d85dfd
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_foreground.png b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_foreground.png
new file mode 100644
index 0000000..e11e0f4
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_foreground.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_round.png b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_round.png
new file mode 100644
index 0000000..fa4a3c6
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xhdpi/fitbot_launcher_round.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher.png b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher.png
new file mode 100644
index 0000000..bf32f32
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_foreground.png b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_foreground.png
new file mode 100644
index 0000000..1d7b0aa
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_foreground.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_round.png b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_round.png
new file mode 100644
index 0000000..7220e5a
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxhdpi/fitbot_launcher_round.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher.png b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher.png
new file mode 100644
index 0000000..8f81dd4
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_foreground.png b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_foreground.png
new file mode 100644
index 0000000..c900079
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_foreground.png differ
diff --git a/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_round.png b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_round.png
new file mode 100644
index 0000000..96cb634
Binary files /dev/null and b/code/src/Fitbot/app/src/main/res/mipmap-xxxhdpi/fitbot_launcher_round.png differ
diff --git a/code/src/Fitbot/app/src/main/res/raw/boxing.qianim b/code/src/Fitbot/app/src/main/res/raw/boxing.qianim
new file mode 100644
index 0000000..f4c9efa
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/raw/boxing.qianim
@@ -0,0 +1 @@
+
diff --git a/code/src/Fitbot/app/src/main/res/raw/chestpress.qianim b/code/src/Fitbot/app/src/main/res/raw/chestpress.qianim
new file mode 100644
index 0000000..56c73b9
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/raw/chestpress.qianim
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/raw/shoulderpress.qianim b/code/src/Fitbot/app/src/main/res/raw/shoulderpress.qianim
new file mode 100644
index 0000000..df2c82e
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/raw/shoulderpress.qianim
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/src/Fitbot/app/src/main/res/values/colors.xml b/code/src/Fitbot/app/src/main/res/values/colors.xml
index f8c6127..001b7ff 100644
--- a/code/src/Fitbot/app/src/main/res/values/colors.xml
+++ b/code/src/Fitbot/app/src/main/res/values/colors.xml
@@ -7,4 +7,13 @@
#FF018786#FF000000#FFFFFFFF
-
\ No newline at end of file
+ #1C1C27
+ #24242F
+ #262630
+ #2C2C37
+ #FFFFFF
+ #000000
+ #000000
+ #00000000
+
+
diff --git a/code/src/Fitbot/app/src/main/res/values/ids.xml b/code/src/Fitbot/app/src/main/res/values/ids.xml
new file mode 100644
index 0000000..f37a0c3
--- /dev/null
+++ b/code/src/Fitbot/app/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/values/strings.xml b/code/src/Fitbot/app/src/main/res/values/strings.xml
index 13c9af6..db32e16 100644
--- a/code/src/Fitbot/app/src/main/res/values/strings.xml
+++ b/code/src/Fitbot/app/src/main/res/values/strings.xml
@@ -4,8 +4,10 @@
Open navigation drawerOpen navigation close
+
Welkom bij FitBotde robot die helpt om fit te blijven
+
StartHelpTODO
@@ -14,7 +16,16 @@
SkipComplete
- 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
- #f22b1d
+ Druk op Start om de oefening te beginnen
+ Ga terug naar het begin scherm door op het huisje te klikken
+ Oefeningen afgerond
+ U heeft de oefeningen voltooid! \n Druk op start om nog een sessie te beginnen
+ Score:
+
+ Title
+ ContextContextContext
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+ Description
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/main/res/values/styles.xml b/code/src/Fitbot/app/src/main/res/values/styles.xml
index b8e7f39..be54ae7 100644
--- a/code/src/Fitbot/app/src/main/res/values/styles.xml
+++ b/code/src/Fitbot/app/src/main/res/values/styles.xml
@@ -2,8 +2,11 @@
+
+
+
\ No newline at end of file
diff --git a/code/src/Fitbot/app/src/test/java/com/example/fitbot/DatabaseFetchingTest.java b/code/src/Fitbot/app/src/test/java/com/example/fitbot/DatabaseFetchingTest.java
new file mode 100644
index 0000000..4f5d387
--- /dev/null
+++ b/code/src/Fitbot/app/src/test/java/com/example/fitbot/DatabaseFetchingTest.java
@@ -0,0 +1,28 @@
+package com.example.fitbot;
+
+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.fetchExerciseFromDatabase();
+ 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---------------------------------");
+
+ }
+
+
+}
diff --git a/docs/documentation/assets/Testkaart-wandeling.png b/docs/documentation/assets/Testkaart-wandeling.png
new file mode 100644
index 0000000..e06d8d9
Binary files /dev/null and b/docs/documentation/assets/Testkaart-wandeling.png differ
diff --git a/docs/documentation/assets/knocksensor.png b/docs/documentation/assets/knocksensor.png
new file mode 100644
index 0000000..4a4907f
Binary files /dev/null and b/docs/documentation/assets/knocksensor.png differ
diff --git a/docs/documentation/brainstorm/usertest.md b/docs/documentation/brainstorm/usertest.md
new file mode 100644
index 0000000..4269910
--- /dev/null
+++ b/docs/documentation/brainstorm/usertest.md
@@ -0,0 +1,10 @@
+# what did we do
+We went on a walk with the members of the Buurtcampus-oost. They host these walks weekly and this on 28 may was their anniversary edition so we did a different route than usual. These walks were originally hosted by the AUAS (HvA). There were elderly people that went on these walks. They first gather at the OBA at linnaeusstraat, and normally they go on a walk around the neighborhood. However, since this was the anniversary edition, we had a different route and activity. We still went on a walk, however we first went with the bus to Amsterdam north, and then we walked to the Landmarkt. This is a market that also has a restaurant, and we had a drink there. We had plenty of time to get to know the elderly people that go on these walks and the students that do projects with these people. We did not want to make these people feel used, so we did not directly ask them questions about our project. However, we did talk and observe them.
+
+# The findings
+While walking to the bus stop, we already saw that these people are happy to move and want to get active. That's why they want to go on these walks together because it is a social way to move. While talking to some of them, it seemed like they want to have something extra that helps them get active. When talking to one of the students, we were talking about our projects, and then the student mentioned to us that they were making a closet with games in it. These games vary from active to board games. Since there were active games there such as ping pong, we asked the student if they would think our project would fit next to the closet as an extra tool. They sounded very excited and told us it would be lovely since it is a unique way to move. Since these elderly people really liked to be social and pepper is a social assertive robot.
+
+These elderly people also seemed very pleased with the latest technology and are not scared of it. We found out they are using all kinds of new and niche technologies, such as apple watches. They also have grandchildren that talk about AI, and they found it very interesting and want to learn more about, however they are scared that it will make people lazy, and some jobs will cease to exist.
+
+# conclusion
+We are glad to see that are projects helps the walking group of the buurtcampus oost. We hope that we can but fitbot next to the closet with all the games and that some of them use it when they are waiting on other people to arrive. These people really seemed to be wanting to get more active but not really to know how to do it, and we are glad to assist them with the use of fitbot.
\ No newline at end of file
diff --git a/docs/documentation/diagrams/assets/positionTracking.png b/docs/documentation/diagrams/assets/positionTracking.png
new file mode 100644
index 0000000..bd712d2
Binary files /dev/null and b/docs/documentation/diagrams/assets/positionTracking.png differ
diff --git a/docs/documentation/diagrams/infrastructure.md b/docs/documentation/diagrams/infrastructure.md
new file mode 100644
index 0000000..bbb68a7
--- /dev/null
+++ b/docs/documentation/diagrams/infrastructure.md
@@ -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()
+ }
+}
+```
+
diff --git a/docs/documentation/diagrams/positionTracking.fzz b/docs/documentation/diagrams/positionTracking.fzz
new file mode 100644
index 0000000..59ba8cf
Binary files /dev/null and b/docs/documentation/diagrams/positionTracking.fzz differ
diff --git a/docs/documentation/hardware/BOM.md b/docs/documentation/hardware/BOM.md
index 78ebbf3..b826a07 100644
--- a/docs/documentation/hardware/BOM.md
+++ b/docs/documentation/hardware/BOM.md
@@ -1,2 +1,14 @@
# BOM
+### Embedded hardware
+
+- [BNO085](https://shop.slimevr.dev/products/slimevr-imu-module-bno085) (IMU)
+- [Custom PCB](https://github.com/Sorakage033/SlimeVR-CheeseCake/tree/main/002-%E2%80%98%E2%80%99Choco%E2%80%98%E2%80%99SpecialRemake) (Use file 8 9 and 10 and pcb thickness 1 mm)
+- [Battery](https://nl.aliexpress.com/item/32583443309.html) (900 mAh)
+- [3D print model](https://github.com/Sorakage033/SlimeVR-CheeseCake/blob/main/004-3D%20Print%20Model/001.3-Chocolate-Case_TypeC-Only.stl)
+
+- [Optional Acrylic lid](https://github.com/Sorakage033/SlimeVR-CheeseCake/blob/main/004-3D%20Print%20Model/acryliclid.svg)
+
+#### Extra notes for assembly
+* Watch out when inserting the assembled product into the case. Make sure you put it in at an angle so you don't break the power switch.
+
diff --git a/docs/documentation/hardware/Ideas.md b/docs/documentation/hardware/Ideas.md
new file mode 100644
index 0000000..8efdd05
--- /dev/null
+++ b/docs/documentation/hardware/Ideas.md
@@ -0,0 +1,17 @@
+# Ideas for hardware
+
+# making a balance bord
+
+Since We are not able to connect the wii fit bord we have to come up with a solution. We thought of it for some time and what we want to do with it. Origanlly we wanted to use the balance bord for excersises such as standing on one leg. This is a simple leg excersise we wanted to have. We thaugt of multiple solutions to still have this excersise. However we still needed to think of a design for the frame.
+
+# the frame
+
+We wanted it to have a similar style to the balance bord. howevere since we can make or own we wanted to make it a bit taller. This makes it easier to implement some other excersise such as the step up. This is na excersise that benefits from a taller box than the wii fit box.
+
+## LDR
+
+We can use a LDR to determine if someone is standing on the bord
+
+## Knock sensor
+
+
\ No newline at end of file
diff --git a/docs/documentation/hardware/Issues.md b/docs/documentation/hardware/Issues.md
index a152afd..18e167d 100644
--- a/docs/documentation/hardware/Issues.md
+++ b/docs/documentation/hardware/Issues.md
@@ -1,4 +1,4 @@
# Issues with hardware
-## Issues with libraries
-The websocket library doesnt work well on the esp8266 d1 mini. It lags out the entire esp and makes it unresponsive.
\ No newline at end of file
+## Issues with programming
+The websocket library doesnt work well on the esp8266 d1 mini. It lags out the entire esp and makes it unresponsive. The solution is to use a different way of communicating.
\ No newline at end of file
diff --git a/docs/documentation/hardware/sensors.md b/docs/documentation/hardware/sensors.md
index ea7d2a3..3bc1629 100644
--- a/docs/documentation/hardware/sensors.md
+++ b/docs/documentation/hardware/sensors.md
@@ -21,7 +21,9 @@ There are a lot of different IMU's with a lot of different specifications.
* Bmi160
### Which one are we gonna use?
+
We are going to use the BNO085 because it has the least amount of drift and its very versatile. We can get almost any type of rotational and acceleration data from it.
+
---
diff --git a/docs/documentation/research-questions/position-tracking-research.md b/docs/documentation/research-questions/position-tracking-research.md
index fd72513..9d0c428 100644
--- a/docs/documentation/research-questions/position-tracking-research.md
+++ b/docs/documentation/research-questions/position-tracking-research.md
@@ -2,25 +2,33 @@
## 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 an embedded system that can track a users position is needed. This system will be used to track their position to determine if a user is doing the exercises correctly.
## Objectives
-* 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.
+- Design an embedded system that can track user position and sent the data to an Android App.
+- Recieve the data from the embedded system in the Android App and sync the data to the current task for the user.
## 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 ESP32 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
-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:
+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:
- 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.
@@ -33,55 +41,58 @@ Accelerometers:
- Cons: Will require additional hardware for data transfer.
- Cost: ~ 5 euros (https://www.amazon.nl/versnellingsmeter-gyroscoop-versnellingssensor-converter-gegevensuitgang/dp/B07BVXN2GP/ref=asc_df_B07BVXN2GP/?tag=nlshogostdde-21&linkCode=df0&hvadid=430548884871&hvpos=&hvnetw=g&hvrand=5187253011954678898&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1010543&hvtargid=pla-928293154057&psc=1&mcid=43bf111afa7b3ba593f4a49321683352)
-### System Requirements
+### Wii Balance Board
-To be added
+The use of a Wii Balance Board was considered for this project. The Wii Balance Board is a balance board that can measure a user's weight and center of balance. The implementation of the board is very similar to the LDR's with an ESP32. The board also had bluetooth which makes it possible to connect to the Android App. However, the Wii Balance Board is very hard to integrate with other systems. The board is not designed to be used with other systems and the documentation is very limited. This makes it very hard to use the board for this project.
+
+#### Problems with the Wii Balance Board
+
+- The Wii Balance Board is not designed to be used with other systems. The documentation is very limited and there is no official SDK available.
+- The Wii Balance Board does not use UUID's for the services and characteristics. This makes it very hard to connect to the board and read the data.
+- When trying to connect to the Wii Balance Board, the board would show up as a device but would ask for a PIN code. The PIN code is not documented anywhere and there is no way to connect to the board without it.
+
+`Bluetooth pairing must be initiated by the host by sending a "Require Authentication" HCI command to its bluetooth device. The bluetooth device will ask the host for a link key, which must be rejected so it will ask for a PIN-Code. The PIN-Code is the binary bluetooth address of the wiimote backwards.`
+
+When trying the solution above the board would reject the PIN code and would not connect to the host.
+
+#### Sources
+
+The following sources have been used to research the Wii Balance Board:
+
+- [Wii Balance Board](https://wiibrew.org/wiki/Wii_Balance_Board#Bluetooth_Communication)
+- [Wii Mote](https://wiibrew.org/wiki/Wiimote#Bluetooth_Communication)
+
+Publicly available projects have been researched aswell nearly all of them dind't use java to connect to the Wii Balance Board. The projects that did use java to connect to the Wii Balance Board were either outdated or didn't work.
## System Design
### Hardware
The hardware of the system will consist of the following components:
- - Wii Fit Board: The primary sensor for position tracking.
- - Pepper: The controller that will process the data from the Wii Fit Board.
+ - LDR: The sensor that will be used to track the user's position based on the light intensity.
+ - ESP32: The microcontroller that will process the data from the LDR.
+ - Pepper: The controller that will recieve the processed data from the ESP32 and will sync the data to the current task for the user.
+
+#### Connection diagram
-### Software
-
-The software of the system will consist of the following:
- - 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
-
-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.
-
-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
### 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.
+A prototype of the app has been created to test the connection between the ESP32 and the Android App. The app can discover BLE devices and connect to them. The app currently is not able to continously read the data from the ESP32. It can however read the data once.
### Testing and Validation
-Tests:
- - 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.
+The prototype of the 'Tracking board' has been tested. The LDR's have were connected to the ESP32 on a breadboard. The ESP32 will emit itself as a GATT server and the Android App will look for the hosts name and connect to it. The Android App will then read the data from the ESP32 once.
## Conclusion
-To be added
+The LDR's with the ESP32 are a good solution for the problem. The LDR's are cheap and easy to use. The ESP32 is a good microcontroller to process the data from the LDR's. The Android App can connect to the ESP32 and read the data from it. The next step is to make the Android App continously read the data from the ESP32 and display it to the user/use it in the app for exercises.
## References
-[Wiiboard lib](https://code.google.com/archive/p/wiiboard-simple/wikis/Documentation.wiki)
-[BlueSoil](https://advanti-lab.sb.dfki.de/?page_id=64)
-[FitScales](https://github.com/paulburton/fitscales)
-[WiiRemoteJ](https://github.com/micromu/WiiRemoteJ)
-[Wiibrew Wiimote](https://wiibrew.org/wiki/Wiimote)
-[Wiibrew Balance Board](https://wiibrew.org/wiki/Wii_Balance_Board)
+[Bluetooth Discovery](https://developer.android.com/develop/connectivity/bluetooth/find-bluetooth-devices)
+[GATT Server Connection](https://developer.android.com/develop/connectivity/bluetooth/ble/connect-gatt-server)
+[ESP32 GATT Server](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils/tests/BLETests/Arduino/BLE_server)
\ No newline at end of file
diff --git a/docs/personal-documentation/sebas/dailyUpdates.md b/docs/personal-documentation/sebas/dailyUpdates.md
index 729a0ae..e7edd15 100644
--- a/docs/personal-documentation/sebas/dailyUpdates.md
+++ b/docs/personal-documentation/sebas/dailyUpdates.md
@@ -294,13 +294,13 @@ To do
Done
- -
+ - skill ontwikkeling plan
**23 May**
To do
- -
+ - NodeJs opzetten
Done
@@ -310,37 +310,30 @@ Done
To do
- -
+ - Pi fixen
+ - Sprint review
+ - Retrospective
Done
- -
+ - Pi fixen
+ - Sprint review
+ - Retrospective
**25 May**
-To do
-
- -
-
-Done
-
- -
+- Weekend
**26 May**
-To do
-
- -
-
-Done
-
- -
+- Weekend / Betoog
**27 May**
To do
- -
+ - Betoog
+ - NodeJs opzetten
Done
@@ -350,27 +343,37 @@ Done
To do
- -
+ - Betoog
+ - NodeJs opzetten
Done
- -
+ - Betoog
+ - NodeJs opzetten
**29 May**
To do
- -
+ - Connectie MariaDB fixen
+ - Ui Style aanpassen en overal het zelfde maken
+ - Ui in het Nederlands
Done
- -
+ - Connectie MariaDB fixen
+ - Ui Styke aanpassen en overal het zelfde maken
+ - Ui in het Nederlands
**30 May**
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
@@ -378,6 +381,136 @@ Done
**31 May**
+To do
+
+ - Expert review
+ - Add Ui for exercises
+
+Done
+
+ - Expert review
+
+**1 June**
+
+- Weekend
+
+**2 June**
+
+- Weekend
+
+**3 June**
+
+To do
+
+ - Add Ui for exercises
+ - Retrieve exercises from database and show them in the app
+
+Done
+
+ - Add Ui for exercises
+
+**4 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**5 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**6 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**7 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**8 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**9 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**10 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**11 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**12 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**13 June**
+
+To do
+
+ -
+
+Done
+
+ -
+
+**14 June**
+
To do
-
diff --git a/docs/personal-documentation/sebas/expertReview/expert3sprint3.md b/docs/personal-documentation/sebas/expertReview/expert3sprint3.md
new file mode 100644
index 0000000..48a98a7
--- /dev/null
+++ b/docs/personal-documentation/sebas/expertReview/expert3sprint3.md
@@ -0,0 +1,57 @@
+# 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.).
+
+### Database connectie
+
+Voor het database gedeelte van K1 ben ik bezig geweest met het maken van een connectie naar de database doormiddel van NodeJs:
+
+[Config](../../../../code/server/test/config.js)
+
+[Connection](../../../../code/server/test/testConnection.js)
+
+### Classes in CPP
+
+Voor het OOP gedeelte van K1 ben ik bezig geweest met het maken en gebruiken van classes in CPP, hierbij heb ik gebruik gemaakt van abstraction en encapsulation:
+
+[CPP Classes](https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66/-/tree/bead6a5a13e5df6658cb9db451c4565250c6a2f6/code/arduino/Position-tracking)
+
+### Java
+
+Omdat wij gebruik maken van java voor het maken van de Android App, is het makkelijk om gebruik te maken van OOP. Java is een object georiënteerde programmeertaal en maakt het makkelijk om gebruik te maken van OOP sinds het een van de belangrijkste concepten is van Java.
+
+In Java heb ik gebruik gemaakt van de volgende OOP concepten:
+
+Abstraction: [Navigation Manager](../../../../code/src/Fitbot/app/src/main/java/com/example/fitbot/util/NavigationManager.java)
+
+In deze class heb ik een public static void gemaakt. Door de void public te maken kan deze functie vanuit elke class worden aangeroepen. Door static te gebruiken kan deze functie worden aangeroepen zonder dat er een object van de class hoeft te worden gemaakt.
+
+De functie is te gebruiker door `NavigationManager.hideSystemUI(this);` aan te roepen. Hierbij is `this` de context van de huidige activity.
+
+---
+
+## K3: Je hebt een infrastructuur ontworpen en gebouwd volgens zelf-gedefinieerde vereisten.
+
+Voor K3 heb ik de feedback op mijn diagrammen van de vorige sprint verwerkt. Deze sprint heb ik research gedaan naar Reverse Proxy, MariaDB en server hosting.
+
+[Infrastructuur met problemen en oplossingen](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.
+
+Voor K4 ben ik bezig geweest met het onderzoeken van hardware die kan samen werken met pepper om de gebruikers positie te tracken. Aan het begin was het idee om met een Wii Balance Board te werken, maar dit was niet mogelijk. Daarom ben ik gaan kijken naar andere hardware waarmee dit mogelijk zo zijn.
+
+[Research naar hardware en software voor de controller](/docs/documentation/research-questions/position-tracking-research.md)
+
+Voor K5 ben ik bezig geweest met het onderzoeken en schrijven van de software die nodig is om de hardware te laten werken. Hierbij heb ik gekeken naar de software die nodig is om de hardware te laten werken en hoe deze samenwerkt met de software van een Android App. Research naar de software die nodig is voor de controller is te vinden in de link hierboven. De code voor de controller is te vinden in de link hieronder.
+
+Code voor [BLE device scanner](https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66/-/blob/bead6a5a13e5df6658cb9db451c4565250c6a2f6/code/src/Fitbot/app/src/main/java/com/example/fitbot/bluetooth/DeviceScanner.java). Deze code is geschreven voor een android applicatie die BLE devices kan discoveren en connecten.
+
+Code voor de [ESP32](https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66/-/blob/bead6a5a13e5df6658cb9db451c4565250c6a2f6/code/arduino/Position-tracking/test/test.ino). Deze code is geschreven om de ESP32 zich te laten opstellen als GATT server en de data van de LDR sensor te versturen naar de android app.
+
+---
\ No newline at end of file