Merge branch 'main' of https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66
33
.idea/workspace.xml
generated
@@ -14,10 +14,9 @@
|
||||
</configurations>
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="00599d5b-7eb5-44da-ad7f-98bf42384c16" name="Changes" comment="Crack butt">
|
||||
<change afterPath="$PROJECT_DIR$/code/web/pepper_data_test.js" afterDir="false" />
|
||||
<list default="true" id="00599d5b-7eb5-44da-ad7f-98bf42384c16" name="Changes" comment="Yebal">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/code/web/incoming_request_handlers.js" beforeDir="false" afterPath="$PROJECT_DIR$/code/web/incoming_request_handlers.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/code/server/incoming_request_handlers.js" beforeDir="false" afterPath="$PROJECT_DIR$/code/server/incoming_request_handlers.js" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -210,7 +209,11 @@
|
||||
<workItem from="1716674767699" duration="21000" />
|
||||
<workItem from="1716889548355" duration="3475000" />
|
||||
<workItem from="1716904079045" duration="2259000" />
|
||||
<workItem from="1716977405893" duration="336000" />
|
||||
<workItem from="1716977405893" duration="466000" />
|
||||
<workItem from="1716983084935" duration="4041000" />
|
||||
<workItem from="1716992784136" duration="136000" />
|
||||
<workItem from="1717093210968" duration="6000" />
|
||||
<workItem from="1717416227033" duration="368000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Changes">
|
||||
<created>1713528225837</created>
|
||||
@@ -384,7 +387,23 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1716891155110</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="23" />
|
||||
<task id="LOCAL-00023" summary="Updated incoming_request_handlers.js to match database querying">
|
||||
<option name="closed" value="true" />
|
||||
<created>1716977853269</created>
|
||||
<option name="number" value="00023" />
|
||||
<option name="presentableId" value="LOCAL-00023" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1716977853269</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00024" summary="Yebal">
|
||||
<option name="closed" value="true" />
|
||||
<created>1716988959836</created>
|
||||
<option name="number" value="00024" />
|
||||
<option name="presentableId" value="LOCAL-00024" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1716988959836</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="25" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -424,6 +443,8 @@
|
||||
<MESSAGE value="Commit crack cocaine" />
|
||||
<MESSAGE value="Commit war crimes in formal Yugoslavia" />
|
||||
<MESSAGE value="Crack butt" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Crack butt" />
|
||||
<MESSAGE value="Updated incoming_request_handlers.js to match database querying" />
|
||||
<MESSAGE value="Yebal" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Yebal" />
|
||||
</component>
|
||||
</project>
|
2
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.configuration.updateBuildConfiguration": "disabled"
|
||||
}
|
@@ -8,23 +8,56 @@ void Connectivity::connectWiFi(char* ssid, char* pass){
|
||||
}
|
||||
}
|
||||
|
||||
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 that will hold the server's IP address
|
||||
|
||||
/** Fetch the IP address of pepper from the server */
|
||||
const char* Connectivity::fetchIPAddress() {
|
||||
char* ipAddress = NULL; // Declare ipAddress as a char*
|
||||
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) {
|
||||
// If successful (code 200), read the response body and parse the IP address
|
||||
String response = http.getString();
|
||||
StaticJsonDocument<200> doc;
|
||||
deserializeJson(doc, response);
|
||||
const char* ip = doc["ip"]; // Extract the IP address
|
||||
ipAddress = strdup(ip);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
}
|
||||
|
||||
http.end();
|
||||
} else {
|
||||
Serial.println("WiFi not connected");
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
|
@@ -1,30 +1,33 @@
|
||||
#ifndef Connectivity_h
|
||||
#define Connectivity_h
|
||||
#ifndef MOVEMENTSENSORCODE_CONNECTIVITY_h
|
||||
#define MOVEMENTSENSORCODE_CONNECTIVITY_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <WebSocketsClient.h>
|
||||
#include <ArduinoWiFiServer.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <ESP8266WiFiGeneric.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
#include <ESP8266WiFiSTA.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
|
||||
// declare the class Connectivity with all functions
|
||||
class Connectivity {
|
||||
public:
|
||||
void connectWiFi(char* ssid, char* pass);
|
||||
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);
|
||||
|
||||
const char* fetchIPAddress();
|
||||
|
||||
private:
|
||||
ESP8266WiFiMulti wifi;
|
||||
WiFiClient wifi_client;
|
||||
WebSocketsClient webSocket;
|
||||
// WebSocketsClient webSocket;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // MOVEMENTSENSORCODE_CONNECTIVITY_h
|
@@ -6,38 +6,42 @@ void setup() {
|
||||
sensorManager.sensorSetup();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
//get data from sensor
|
||||
SensorManager::eulerAngles Rotation = sensorManager.getEulerAngles();
|
||||
|
||||
//static structure
|
||||
// TODO: redo json for esp8266 and in android studio
|
||||
struct acceleration {
|
||||
float x = 9;
|
||||
float y = 9;
|
||||
float z = 9;
|
||||
} accelData;
|
||||
|
||||
if (!ipAquired) {
|
||||
serverIp = connectivity.fetchIPAddress(); //Fetch pepper ip address
|
||||
ipAquired = true;
|
||||
}
|
||||
|
||||
unsigned long lastTime = 0; // will store the last time the code was run
|
||||
unsigned long currentTime = millis();
|
||||
if (currentTime - lastTime >= 100) { // 100 ms has passed
|
||||
if (currentTime - lastTime >= 100) { // do everything inside every 100 ms
|
||||
memset(buffer, 0, BUFFER_SIZE);
|
||||
//convert string to char*
|
||||
sprintf(
|
||||
buffer,
|
||||
"{\"deviceId\": %d, \"rotationX\": %f, \"rotationY\": %f, \"rotationZ\": %f, \"accelerationX\": %f, \"accelerationY\": %f, \"accelerationZ\": %f, \"type\": %s}",
|
||||
DEVICE_ID,
|
||||
eulerRotation.roll,
|
||||
eulerRotation.pitch,
|
||||
eulerRotation.yaw,
|
||||
Rotation.roll,
|
||||
Rotation.pitch,
|
||||
Rotation.yaw,
|
||||
accelData.x,
|
||||
accelData.y,
|
||||
accelData.z,
|
||||
"data");
|
||||
// %d = int, %f = floatation, %s = string
|
||||
connectivity.httpPost("192.168.137.243", "/", 3445, buffer, strlen(buffer), "application/json");
|
||||
//send data to pepper
|
||||
connectivity.httpPost(serverIp, "/", 3445, buffer, strlen(buffer), "application/json");
|
||||
lastTime = currentTime;
|
||||
}
|
||||
}
|
||||
//acceleration.X
|
||||
//acceleration.Y
|
||||
//acceleration.Z
|
||||
|
@@ -17,14 +17,14 @@ void SensorManager::sensorSetup() {
|
||||
// myIMU.enableStepCounter(500); //Send data update every 500ms
|
||||
}
|
||||
//get sensordata
|
||||
SensorManager::RotationQuintillions SensorManager::getQuintillions() {
|
||||
SensorManager::RotationQuaternions SensorManager::getQuaternions() {
|
||||
if (myIMU.dataAvailable() == true) {
|
||||
float i = myIMU.getQuatI();
|
||||
float j = myIMU.getQuatJ();
|
||||
float k = myIMU.getQuatK();
|
||||
float w = myIMU.getQuatReal();
|
||||
|
||||
RotationQuintillions rotation = { i, j, k, w };
|
||||
RotationQuaternions rotation = { i, j, k, w };
|
||||
return rotation;
|
||||
} else {
|
||||
float i = myIMU.getQuatI();
|
||||
@@ -32,13 +32,13 @@ SensorManager::RotationQuintillions SensorManager::getQuintillions() {
|
||||
float k = myIMU.getQuatK();
|
||||
float w = myIMU.getQuatReal();
|
||||
|
||||
RotationQuintillions rotation = { i, j, k, w };
|
||||
RotationQuaternions rotation = { i, j, k, w };
|
||||
return rotation;
|
||||
}
|
||||
}
|
||||
//calculate Quintillions to Euler angles from -1π to +1π
|
||||
//calculate Quaternions to Euler angles from -1π to +1π
|
||||
SensorManager::eulerAngles SensorManager::getEulerAngles() {
|
||||
SensorManager::RotationQuintillions rotation = getQuintillions();
|
||||
SensorManager::RotationQuaternions rotation = getQuaternions();
|
||||
float roll = atan2(2.0f * (rotation.w * rotation.i + rotation.j * rotation.k), 1.0f - 2.0f * (rotation.i * rotation.i + rotation.j * rotation.j));
|
||||
float pitch = asin(2.0f * (rotation.w * rotation.j - rotation.k * rotation.i));
|
||||
float yaw = atan2(2.0f * (rotation.w * rotation.k + rotation.i * rotation.j), 1.0f - 2.0f * (rotation.j * rotation.j + rotation.k * rotation.k));
|
||||
|
@@ -1,9 +1,10 @@
|
||||
#ifndef SensorManager_h
|
||||
#define SensorManager_h
|
||||
#ifndef MOVEMENTSENSORCODE_SENSORMANAGER_H
|
||||
#define MOVEMENTSENSORCODE_SENSORMANAGER_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "SparkFun_BNO080_Arduino_Library.h"
|
||||
|
||||
// declare the class SensorManager with all functions
|
||||
class SensorManager {
|
||||
public:
|
||||
SensorManager();
|
||||
@@ -18,19 +19,22 @@ public:
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
// void sendData(float roll, float pitch, float yaw);
|
||||
eulerAngles getEulerAngles();
|
||||
acceleration getAcelleration();
|
||||
bool sensorTap();
|
||||
|
||||
private:
|
||||
struct RotationQuintillions {
|
||||
|
||||
struct RotationQuaternions {
|
||||
float i;
|
||||
float j;
|
||||
float k;
|
||||
float w;
|
||||
};
|
||||
RotationQuintillions getQuintillions();
|
||||
RotationQuaternions getQuaternions(); // get the quaternions from the sensor
|
||||
|
||||
BNO080 myIMU;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // MOVEMENTSENSORCODE_SENSORMANAGER_H
|
@@ -11,5 +11,8 @@ WebSocketsClient webSocket;
|
||||
#define ssid "1235678i"
|
||||
#define pass "12345678"
|
||||
#define BUFFER_SIZE 1024
|
||||
#define DEVICE_ID 1
|
||||
#define DEVICE_ID 0
|
||||
|
||||
char *buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
|
||||
const char* serverIp = NULL; // Declare serverIp here
|
||||
bool ipAquired = false;
|
@@ -1,33 +0,0 @@
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// 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
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
const databaseQuery = 'SELECT * FROM `Exercise` WHERE ExerciseID = 1';
|
||||
// const databaseQueryRand = 'SELECT * FROM `Exercise` ORDER BY RAND() LIMIT 1';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -9,41 +11,33 @@
|
||||
function handleIncoming(request, response, app, pool)
|
||||
{
|
||||
|
||||
let query = 'SELECT * FROM Exercise WHERE ExerciseID = ?';
|
||||
let parameters = [];
|
||||
|
||||
if (!request.hasOwnProperty('uid') || typeof request.uid !== 'number')
|
||||
{
|
||||
query = 'SELECT * FROM Exercise';
|
||||
} else parameters.push(request.uid);
|
||||
|
||||
// Acquire database connection
|
||||
pool.getConnection()
|
||||
.then(conn => {
|
||||
conn.query(query, parameters)
|
||||
conn.query(
|
||||
databaseQuery)
|
||||
.then(rows => {
|
||||
if (rows.length === 0)
|
||||
{
|
||||
response
|
||||
.status(404)
|
||||
.send(JSON.stringify({error: 'Exercise not found'}));
|
||||
.send(JSON.stringify({error: 'No exercises found in the database.'}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back the data in the right format
|
||||
let converted = rows.map(row => {
|
||||
return {
|
||||
name: row.Name,
|
||||
description: row.Description,
|
||||
muscleGroup: row.MuscleGroup,
|
||||
imageUrl: row.ImageURL,
|
||||
videoUrl: row.VideoURL
|
||||
};
|
||||
});
|
||||
|
||||
response
|
||||
.status(200)
|
||||
.send(JSON.stringify(converted));
|
||||
let row = rows[0];
|
||||
response.status(200)
|
||||
.send(JSON.stringify({
|
||||
exerciseId: row['ExerciseID'],
|
||||
name: row['Name'],
|
||||
muscleGroup: row['MuscleGroup'],
|
||||
shortDescription: row['ShortDescription'],
|
||||
description: row['Description'],
|
||||
imageUrl: row['ImageURL'],
|
||||
videoUrl: row['VideoURL'],
|
||||
path: row['Path'],
|
||||
duration: row['Duration']
|
||||
}))
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
22
code/server/ipSender/index.js
Normal file
@@ -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');
|
||||
});
|
@@ -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}`);
|
8
code/server/test/config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// config.js
|
||||
module.exports = {
|
||||
host: '127.0.0.1',
|
||||
user: 'fitbot',
|
||||
password: 'fitbot123',
|
||||
database: 'fitbot',
|
||||
port: 3306, // Default MariaDB port
|
||||
};
|
35
code/server/test/testConnection.js
Normal file
@@ -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}`);
|
||||
});
|
1
code/src/Fitbot/.gitignore
vendored
@@ -15,3 +15,4 @@
|
||||
local.properties
|
||||
.idea
|
||||
.vscode
|
||||
/.idea/
|
||||
|
14
code/src/Fitbot/.idea/misc.xml
generated
@@ -16,33 +16,45 @@
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml" value="0.165" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_endscreen.xml" value="0.1" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.1234375" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_help.xml" value="0.1" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.1234375" />
|
||||
<entry key="..\:/Users/Niels/muupooviixee66-3/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.1234375" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.25" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/big_red_button_gradient.xml" value="0.2555" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/border_background.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/border_background_2.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/border_background_3.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/border_background_circle.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/box_background.xml" value="0.2555" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/darkred_button_gradient.xml" value="0.346" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/fitbot_launcher_background.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/help2_background.xml" value="0.2395" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/help_background.xml" value="0.2395" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_home_48.xml" value="0.25" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_settings_48.xml" value="0.25" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_baseline_star_rate_48.xml" value="0.25" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/ic_launcher_background.xml" value="0.25" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/progress_circle.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/progress_circle_burst.xml" value="0.2475" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/progress_circle_burst_good.xml" value="0.1" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/rectangle.xml" value="0.2395" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/drawable/red_button_gradient.xml" value="0.346" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_bicepvideo.xml" value="0.22826086956521738" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_end_screen.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.6" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_help.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_info_dialog.xml" value="0.264" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_main_screen.xml" value="0.1" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_power_screen.xml" value="0.1" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_item.xml" value="0.1" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_menu.xml" value="0.1" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/dialog_info.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.264" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.264" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/menu/main_menu.xml" value="0.4" />
|
||||
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/mipmap-anydpi-v26/fitbot_launcher.xml" value="0.2475" />
|
||||
<entry key="app/src/main/res/layout/activity_end_screen.xml" value="0.1" />
|
||||
<entry key="app/src/main/res/layout/activity_fitness.xml" value="0.23550724637681159" />
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||
|
@@ -10,19 +10,21 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:icon="@mipmap/fitbot_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:roundIcon="@mipmap/fitbot_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
|
BIN
code/src/Fitbot/app/src/main/fitbot_launcher-playstore.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
code/src/Fitbot/app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 94 KiB |
@@ -19,11 +19,10 @@ public enum EMuscleGroup {
|
||||
return this.muscleGroupIdentifier;
|
||||
}
|
||||
|
||||
public static EMuscleGroup parse(String name)
|
||||
{
|
||||
for ( EMuscleGroup muscleGroup : EMuscleGroup.values())
|
||||
for ( String muscleGroupName : muscleGroup.muscleGroupNames)
|
||||
if ( muscleGroupName.equalsIgnoreCase(name))
|
||||
public static EMuscleGroup parse(String name) {
|
||||
for (EMuscleGroup muscleGroup : EMuscleGroup.values())
|
||||
for (String muscleGroupName : muscleGroup.muscleGroupNames)
|
||||
if (muscleGroupName.equalsIgnoreCase(name))
|
||||
return muscleGroup;
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,19 +1,14 @@
|
||||
package com.example.fitbot.exercise;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.fitbot.util.path.GesturePath;
|
||||
import com.example.fitbot.util.server.IWebServerHandler;
|
||||
import com.example.fitbot.util.server.WebServer;
|
||||
|
||||
import java.util.Objects;
|
||||
import com.example.fitbot.util.path.AnglePath;
|
||||
|
||||
public class Exercise {
|
||||
|
||||
public final EMuscleGroup muscleGroup;
|
||||
public final GesturePath leftPath;
|
||||
public final GesturePath rightPath;
|
||||
public final String title;
|
||||
public final AnglePath leftPath;
|
||||
public final AnglePath rightPath;
|
||||
public final String name;
|
||||
public final String shortDescription;
|
||||
public final String description;
|
||||
public final String imageUrl;
|
||||
public final String videoUrl;
|
||||
@@ -22,17 +17,22 @@ public class Exercise {
|
||||
/**
|
||||
* Constructor for the AbstractExercise class.
|
||||
*
|
||||
* @param muscleGroup The muscle group of the exercise.
|
||||
* @param leftPath The path of the left hand.
|
||||
* @param rightPath The path of the right hand.
|
||||
* @param title The title of the exercise.
|
||||
* @param description The description of the exercise.
|
||||
* @param imageUrl The URL of the image.
|
||||
* @param videoUrl The URL of the video.
|
||||
* @param muscleGroup The muscle group of the exercise.
|
||||
* @param leftPath The path of the left hand.
|
||||
* @param rightPath The path of the right hand.
|
||||
* @param name The title of the exercise.
|
||||
* @param shortDescription The short description of the exercise.
|
||||
* @param description The full description of the exercise.
|
||||
* @param imageUrl The URL of the image.
|
||||
* @param videoUrl The URL of the video.
|
||||
*/
|
||||
public Exercise(EMuscleGroup muscleGroup, String title, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) {
|
||||
public Exercise(EMuscleGroup muscleGroup, String name, String shortDescription,
|
||||
String description, String imageUrl, String videoUrl,
|
||||
AnglePath leftPath, AnglePath rightPath, float exerciseTimeInSeconds) {
|
||||
|
||||
this.name = name;
|
||||
this.muscleGroup = muscleGroup;
|
||||
this.title = title;
|
||||
this.shortDescription = shortDescription;
|
||||
this.description = description;
|
||||
this.leftPath = leftPath;
|
||||
this.rightPath = rightPath;
|
||||
@@ -40,4 +40,8 @@ public class Exercise {
|
||||
this.videoUrl = videoUrl;
|
||||
this.exerciseTimeInSeconds = exerciseTimeInSeconds;
|
||||
}
|
||||
|
||||
public interface ExerciseFetchHandler {
|
||||
void handle(Exercise exercise);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package com.example.fitbot.exercise;
|
||||
|
||||
import com.example.fitbot.util.path.GesturePath;
|
||||
import com.example.fitbot.util.path.AnglePath;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
@@ -13,37 +13,46 @@ import java.net.URLConnection;
|
||||
|
||||
public class ExerciseManager {
|
||||
|
||||
private static final String HOST_ADDRESS = "http://145.92.8.132:443/";
|
||||
private static final String HOST_ADDRESS = "http://145.92.8.132:443";
|
||||
|
||||
// The value of these property variables must be equivalent of
|
||||
// the JSON data that the database sends back.
|
||||
// If this is not the case then the exercise retrieval will fail.
|
||||
private static final String PROPERTY_EXERCISE_DURATION = "duration";
|
||||
private static final String PROPERTY_EXERCISE_ID = "exerciseId";
|
||||
private static final String PROPERTY_MUSCLE_GROUP = "muscleGroup";
|
||||
private static final String PROPERTY_DESC = "description";
|
||||
private static final String PROPERTY_IMAGE_URL = "imageUrl";
|
||||
private static final String PROPERTY_VIDEO_URL = "videoUrl";
|
||||
private static final String PROPERTY_DESC = "description";
|
||||
private static final String PROPERTY_SHORT_DESC = "shortDescription";
|
||||
private static final String PROPERTY_PATH = "path";
|
||||
private static final String PROPERTY_NAME = "name";
|
||||
private static final String PROPERTY_DATA = "data";
|
||||
private static final String PROPERTY_EXERCISE_DURATION = "exerciseDuration";
|
||||
|
||||
public static final int SENSOR_COUNT = 2;
|
||||
|
||||
private static final String[] REQUIRED_PROPERTIES = {
|
||||
PROPERTY_MUSCLE_GROUP, PROPERTY_DESC, PROPERTY_IMAGE_URL,
|
||||
PROPERTY_VIDEO_URL, PROPERTY_NAME, PROPERTY_DATA,
|
||||
PROPERTY_EXERCISE_DURATION
|
||||
PROPERTY_MUSCLE_GROUP, PROPERTY_DESC,PROPERTY_SHORT_DESC, PROPERTY_IMAGE_URL,
|
||||
PROPERTY_VIDEO_URL, PROPERTY_NAME, PROPERTY_PATH,
|
||||
PROPERTY_EXERCISE_DURATION, PROPERTY_EXERCISE_ID
|
||||
};
|
||||
|
||||
private static final float DEFAULT_SEGMENT_SPEED = 1.0f;
|
||||
public static final int DEFAULT_EXERCISE_REPETITIONS = 10;
|
||||
public static final float EXERCISE_ERROR_MARGIN = 1.5f;
|
||||
public static final float EXERCISE_TIME_SCALING_FACTOR = 1.0f;
|
||||
|
||||
// Fields representing the statistics of the user
|
||||
public static int TOTAL_REPETITIONS_REQUIRED = 0;
|
||||
public static int TOTAL_REPETITIONS_PERFORMED = 0;
|
||||
public static int TOTAL_EXERCISES_PREFORMED = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Function for sending an HTTP request to the server.
|
||||
*
|
||||
* @param url The URL to send the request to.
|
||||
* @param method The method to use for the request, e.g. GET or POST.
|
||||
* @param url The URL to send the request to.
|
||||
* @param method The method to use for the request, e.g. GET or POST.
|
||||
* @param contentType The content type of the request.
|
||||
* @param body The body of the request.
|
||||
*
|
||||
* @param body The body of the request.
|
||||
* @return The response from the server.
|
||||
*/
|
||||
public static String sendHTTP(String url, String method, String contentType, String body) {
|
||||
@@ -56,7 +65,8 @@ public class ExerciseManager {
|
||||
connection.connect();
|
||||
// Send a body if it is present
|
||||
if (body != null)
|
||||
connection.getOutputStream().write(body.getBytes());
|
||||
connection.getOutputStream().write(body.getBytes());
|
||||
|
||||
InputStream stream = connection.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -76,18 +86,20 @@ public class ExerciseManager {
|
||||
*
|
||||
* @return The exercise, if it exists on the server. Otherwise null.
|
||||
*/
|
||||
public static Exercise retrieveExercise() {
|
||||
public static Exercise fetchExerciseFromDatabase() {
|
||||
String response = sendHTTP(
|
||||
HOST_ADDRESS, "POST", "application/json", null
|
||||
HOST_ADDRESS, "POST", "application/json", "{}"
|
||||
);
|
||||
// Validate the response
|
||||
if (response != null) {
|
||||
try {
|
||||
System.out.println(response);
|
||||
JsonObject content = JsonParser.parseString(response).getAsJsonObject();
|
||||
|
||||
// Ensure all required properties are present
|
||||
for (String property : REQUIRED_PROPERTIES) {
|
||||
if (!content.has(property)) {
|
||||
System.out.println("Missing property: " + property);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -95,20 +107,25 @@ public class ExerciseManager {
|
||||
// Path data is split into two parts, due to the left and right hand.
|
||||
// If one wants to add support for more sensors, one will have to adjust the Exercise
|
||||
// class to support more paths.
|
||||
String[] leftRightData = content.get(PROPERTY_DATA).getAsString().split(";");
|
||||
System.out.println(content.get(PROPERTY_PATH).getAsString());
|
||||
|
||||
if ( leftRightData.length != SENSOR_COUNT)
|
||||
AnglePath[] paths = AnglePath.fromString(content.get(PROPERTY_PATH).getAsString());
|
||||
|
||||
if (paths.length != SENSOR_COUNT) {
|
||||
System.out.println("Invalid path data.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Exercise(
|
||||
EMuscleGroup.parse(content.get(PROPERTY_MUSCLE_GROUP).getAsString()),
|
||||
content.get(PROPERTY_NAME).getAsString(),
|
||||
content.get(PROPERTY_SHORT_DESC).getAsString(),
|
||||
content.get(PROPERTY_DESC).getAsString(),
|
||||
content.get(PROPERTY_IMAGE_URL).getAsString(),
|
||||
content.get(PROPERTY_VIDEO_URL).getAsString(),
|
||||
GesturePath.fromString(leftRightData[0]),
|
||||
GesturePath.fromString(leftRightData[1]),
|
||||
DEFAULT_SEGMENT_SPEED
|
||||
paths[0],
|
||||
paths[1],
|
||||
content.get(PROPERTY_EXERCISE_DURATION).getAsInt()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@@ -1,53 +0,0 @@
|
||||
package com.example.fitbot.exercise;
|
||||
|
||||
|
||||
/**
|
||||
* The ExerciseUser class represents a user of the exercise application.
|
||||
* This contains all necessary information of the current user.
|
||||
*/
|
||||
public class ExerciseUser {
|
||||
|
||||
public float upperArmLength;
|
||||
public float lowerArmLength;
|
||||
public float upperLegLength;
|
||||
public float lowerLegLength;
|
||||
public float height;
|
||||
|
||||
/**
|
||||
* Constructor for the ExerciseUser class.
|
||||
* @param upperArmLength The length of the upper arm.
|
||||
* @param lowerArmLength The length of the lower arm.
|
||||
* @param height The height of the user.
|
||||
* @param upperLegLength The length of the upper leg.
|
||||
* @param lowerLegLength The length of the lower leg.
|
||||
*/
|
||||
public ExerciseUser(float upperArmLength, float lowerArmLength, float height, float upperLegLength, float lowerLegLength) {
|
||||
this.upperArmLength = upperArmLength;
|
||||
this.lowerArmLength = lowerArmLength;
|
||||
this.upperLegLength = upperLegLength;
|
||||
this.lowerLegLength = lowerLegLength;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for the ExerciseUser class.
|
||||
* @param height The height of the user.
|
||||
*/
|
||||
public ExerciseUser(float height) {
|
||||
this.height = height;
|
||||
this.upperArmLength = height * 0.2f;
|
||||
this.lowerArmLength = height * 0.2f;
|
||||
this.upperLegLength = height * 0.3f;
|
||||
this.lowerLegLength = height * 0.3f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for the ExerciseUser class.
|
||||
* This sets the default height to 180.0f. (1.80m)
|
||||
*/
|
||||
public ExerciseUser() {
|
||||
this(180.0f);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
public abstract class AbstractPepperActionEvent {
|
||||
|
||||
public abstract EPepperAction getAction();
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
/**
|
||||
* Enum for Pepper actions
|
||||
*/
|
||||
public enum EPepperAction {
|
||||
ACTION_SPEAK,
|
||||
ACTION_ANIMATE,
|
||||
NO_ACTION
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
package com.example.fitbot.pepper;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
import com.aldebaran.qi.sdk.RobotLifecycleCallbacks;
|
||||
import com.aldebaran.qi.sdk.object.locale.Language;
|
||||
import com.aldebaran.qi.sdk.object.locale.Locale;
|
||||
import com.aldebaran.qi.sdk.object.locale.Region;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Class representing the Pepper robot.
|
||||
* All functionality related to Pepper is implemented in this class.
|
||||
* <p>
|
||||
* Before any of the functions in this class can be used, the Pepper class needs a QiContext to be
|
||||
* set. This is retrieved in the `onRobotFocusGained` method of the RobotLifecycleCallbacks
|
||||
* interface, and can then provided using the `provideContext` method.
|
||||
* </p>
|
||||
*/
|
||||
public class Pepper {
|
||||
|
||||
// Queue containing all Pepper actions that need to be executed
|
||||
// This is to prevent the app from crashing when attempting to perform multiple actions at once
|
||||
private static final ConcurrentLinkedQueue<AbstractPepperActionEvent> pepperActionEventQueue =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
private static final AtomicBoolean isAnimating = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean isSpeaking = new AtomicBoolean(false);
|
||||
|
||||
private static final 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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 EPepperAction.ACTION_ANIMATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
package com.example.fitbot.ui.activities;
|
||||
|
||||
public class CompletionActivity {
|
||||
}
|
@@ -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,8 @@ public class EndScreenActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_end_screen);
|
||||
|
||||
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonEndScreen, MainActivity.class);
|
||||
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.startButtonEndScreen, FitnessActivity.class);
|
||||
NavigationManager.hideSystemUI(this);
|
||||
|
||||
NavigationManager.setupButtonNavigation(this, R.id.homeButtonEndScreen, MainActivity.class);
|
||||
}
|
||||
}
|
@@ -1,8 +1,23 @@
|
||||
package com.example.fitbot.ui.activities;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.VideoView;
|
||||
|
||||
import com.aldebaran.qi.sdk.QiContext;
|
||||
@@ -10,123 +25,275 @@ import com.aldebaran.qi.sdk.QiSDK;
|
||||
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.aldebaran.qi.sdk.object.actuation.Animate;
|
||||
import com.example.fitbot.R;
|
||||
import com.example.fitbot.exercise.Exercise;
|
||||
import com.example.fitbot.exercise.ExerciseManager;
|
||||
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
|
||||
import com.example.fitbot.util.ButtonNavigation;
|
||||
import com.example.fitbot.util.FitnessCycle;
|
||||
import com.example.fitbot.pepper.Pepper;
|
||||
import com.example.fitbot.util.NavigationManager;
|
||||
import com.example.fitbot.util.processing.InputProcessor;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
|
||||
|
||||
// Private fields for the FitnessActivity class.
|
||||
private PersonalMotionPreviewElement personalMotionPreviewElement;
|
||||
private InputProcessor motionProcessor;
|
||||
private Exercise currentExercise;
|
||||
|
||||
// Progress circle for the exercises
|
||||
private ProgressBar progressCircle;
|
||||
private TextView progressText;
|
||||
private int progress = 1;
|
||||
private final int maxProgress = 10;
|
||||
|
||||
// Exercise status element data
|
||||
private TextView exerciseMuscleGroupTextView;
|
||||
private TextView exerciseNameTextView;
|
||||
private TextView exerciseShortDescriptionTextView;
|
||||
//private TextView exerciseDescriptionTextView;
|
||||
private static String exerciseVideoUrl;
|
||||
private Animate animate;
|
||||
private VideoView videoView;
|
||||
private QiContext qiContext;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
// Some nice little messages for the user
|
||||
private static final String[] EXERCISE_NOT_FOUND_MESSAGES = new String[] {
|
||||
private static final String[] EXERCISE_NOT_FOUND_MESSAGES = new String[]{
|
||||
"Ik heb momenteel helaas wat moeite met het ophalen van oefeningen, sorry.",
|
||||
"Het lijkt erop dat de oefeningen op een misterieus avontuur zijn. Even wachten tot ze terug zijn.",
|
||||
"Ssst, de oefeningen slapen nog, probeer het later nog eens."
|
||||
", de oefeningen slapen nog, probeer het later nog eens."
|
||||
};
|
||||
|
||||
private static final String EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE =
|
||||
"Indien dit probleem zich voortzet, neem contact op met de ontwikkelaar.";
|
||||
|
||||
private static final float SENSOR_SAMPLE_RATE = 10.0f;
|
||||
private static final int EXERCISE_COUNT = 5;
|
||||
private static final int EXERCISE_COUNT = 5;
|
||||
private static int EXERCISE_VIDEO_REPETITION = 1;
|
||||
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);
|
||||
videoView = findViewById(R.id.videoView);
|
||||
|
||||
// Remove the ugly ass bar on top of the view
|
||||
// Fill empty objects with exercise data
|
||||
this.exerciseNameTextView = findViewById(R.id.textViewFitnessTitle);
|
||||
this.exerciseShortDescriptionTextView = findViewById(R.id.textViewFitnessShortDescription);
|
||||
//this.exerciseDescriptionTextView = findViewById(R.id.textViewDialogDescription);
|
||||
|
||||
// Set the repetition count for the exercise
|
||||
EXERCISE_VIDEO_REPETITION = 1;
|
||||
|
||||
progressCircle = findViewById(R.id.progressCircle);
|
||||
progressText = findViewById(R.id.progressText);
|
||||
progressCircle.setMax(maxProgress);
|
||||
|
||||
// Set color of loading circle
|
||||
ProgressBar loadingCircle = findViewById(R.id.loadingCircle);
|
||||
loadingCircle.setIndeterminateTintList(ColorStateList.valueOf(Color.RED));
|
||||
|
||||
// Navigation Buttons
|
||||
NavigationManager.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
|
||||
NavigationManager.setupButtonNavigation(this, R.id.skipButtonFitness, MainActivity.class); //Needs to skip exercises once those are implemented
|
||||
|
||||
// Hide system UI
|
||||
NavigationManager.hideSystemUI(this);
|
||||
setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy.IMMERSIVE);
|
||||
|
||||
// Find the VideoView by its ID
|
||||
VideoView videoView = findViewById(R.id.videoView);
|
||||
FitnessCycle.playVideo(videoView, this);
|
||||
ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonFitness, MainActivity.class);
|
||||
// Implement your logic when the robot focus is gained
|
||||
|
||||
// Initiate info button
|
||||
Button infoButtonFitness = findViewById(R.id.infoButtonFitness);
|
||||
infoButtonFitness.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showInfoDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRobotFocusGained(QiContext qiContext) {
|
||||
this.qiContext = qiContext;
|
||||
// Find the VideoView by its ID
|
||||
// CompletableFuture.runAsync(() -> FitnessCycle.executeMovement("bicepcurl", 10, qiContext));
|
||||
|
||||
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
|
||||
// Provide the context so that all queued actions can be performed.
|
||||
Pepper.provideContext(qiContext, this.getClass());
|
||||
|
||||
// Initialize the element whenever it has been added to the screen.
|
||||
// This will provide the element with the appropriate dimensions for drawing
|
||||
// the canvas properly.
|
||||
personalMotionPreviewElement.post(() -> {
|
||||
Exercise exercise = this.acquireExercise();
|
||||
if ( exercise == null) {
|
||||
return;
|
||||
}
|
||||
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(this);
|
||||
motionProcessor.useExercise(exercise);
|
||||
|
||||
motionProcessor = new InputProcessor(vectors, exercise.exerciseTimeInSeconds, SENSOR_SAMPLE_RATE);
|
||||
|
||||
personalMotionPreviewElement.provideQiContext(qiContext);
|
||||
personalMotionPreviewElement.initialize(exercise, motionProcessor, EXERCISE_COUNT);
|
||||
|
||||
motionProcessor.startListening();
|
||||
motionProcessor.setInputHandler(personalMotionPreviewElement);
|
||||
}, (n) -> {
|
||||
int randomMessageIndex = (int) Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||
Pepper.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex]);
|
||||
Pepper.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE);
|
||||
// Run on main thread to prevent crashes (wack)
|
||||
this.runOnUiThread(() -> NavigationManager.navigateToActivity(this, EndScreenActivity.class));
|
||||
});
|
||||
personalMotionPreviewElement.provideQiContext(qiContext);
|
||||
|
||||
// FitnessCycle.playVideo(qiContext, videoView, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire an exercise from the ExerciseManager.
|
||||
* Whenever the retrieval failed, it will have the robot say something to the user
|
||||
* to inform them about the issue.
|
||||
*
|
||||
* @return The acquired exercise, or null if the exercise could not be retrieved.
|
||||
*/
|
||||
public Exercise acquireExercise() {
|
||||
Exercise exercise = ExerciseManager.retrieveExercise();
|
||||
if ( exercise == null && this.qiContext != null)
|
||||
{
|
||||
int randomMessageIndex = (int)Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
|
||||
FitnessCycle.say(EXERCISE_NOT_FOUND_MESSAGES[randomMessageIndex], qiContext);
|
||||
FitnessCycle.say(EXERCISE_NOT_FOUND_SEEK_HELP_MESSAGE, qiContext);
|
||||
public void fetchExerciseAsync(Exercise.ExerciseFetchHandler onSuccessfulFetch, Exercise.ExerciseFetchHandler onFailedFetch) {
|
||||
// For some stupid reason we cannot perform network operations on the main thread.
|
||||
Log.e("fetchvideo", "video fetched");
|
||||
// therefore we'll have to do it like this...
|
||||
new Thread(() -> {
|
||||
Exercise exercise = ExerciseManager.fetchExerciseFromDatabase();
|
||||
if (exercise == null) {
|
||||
onFailedFetch.handle(null);
|
||||
|
||||
// Main thread stuff
|
||||
runOnUiThread(() -> NavigationManager.navigateToActivity(this, MainActivity.class));
|
||||
} else {
|
||||
onSuccessfulFetch.handle(exercise);
|
||||
runOnUiThread(() -> {
|
||||
|
||||
exerciseNameTextView.setText(exercise.name);
|
||||
exerciseShortDescriptionTextView.setText(exercise.shortDescription);
|
||||
// exerciseDescriptionTextView.setText(exercise.description);
|
||||
exerciseVideoUrl = exercise.videoUrl;
|
||||
|
||||
// Play the video
|
||||
playVideo(videoView, this);
|
||||
|
||||
// When the video has started playing remove the loading circle
|
||||
videoView.setOnInfoListener((mp, what, extra) -> {
|
||||
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
|
||||
ProgressBar loadingCircle = findViewById(R.id.loadingCircle);
|
||||
loadingCircle.setVisibility(View.GONE);
|
||||
|
||||
|
||||
//Pepper.animate("armraise");
|
||||
|
||||
// Start checking for user movement once the video has loaded
|
||||
this.motionProcessor.startListening();
|
||||
this.motionProcessor.startCheckingUserMovement();
|
||||
this.motionProcessor.setRecording(true, 3.f);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
videoView.setOnCompletionListener(mp -> {
|
||||
if (EXERCISE_VIDEO_REPETITION < ExerciseManager.DEFAULT_EXERCISE_REPETITIONS) {
|
||||
videoView.start(); // start the video again
|
||||
EXERCISE_VIDEO_REPETITION++;
|
||||
if (progress >= 10) {
|
||||
runOnUiThread(() -> NavigationManager.navigateToActivity(this, EndScreenActivity.class));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}).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 void playVideo(VideoView videoView, Context context) {
|
||||
// Set up the video player
|
||||
if (videoView != null) {
|
||||
videoView.setVideoURI(Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.arm_raises));
|
||||
videoView.start();
|
||||
} else {
|
||||
Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
|
||||
}
|
||||
return exercise;
|
||||
}
|
||||
|
||||
@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.motionProcessor.stopListening();
|
||||
this.motionProcessor = null;
|
||||
this.personalMotionPreviewElement.destroy();
|
||||
Pepper.provideContext(null, this.getClass());
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void showInfoDialog() {
|
||||
final Dialog dialog = new Dialog(this);
|
||||
dialog.setContentView(R.layout.dialog_info);
|
||||
|
||||
// Hide the system UI
|
||||
NavigationManager.hideSystemUI(this);
|
||||
|
||||
// Set the dialog options
|
||||
dialog.getWindow().setDimAmount(0.5f);
|
||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
|
||||
dialog.setCancelable(true);
|
||||
|
||||
// Close button for dialog
|
||||
Button closeButton = dialog.findViewById(R.id.closeButtonDialog);
|
||||
closeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void incrementProgress() {
|
||||
if (progress++ < maxProgress) {
|
||||
triggerColorBurst(true);
|
||||
updateProgress();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProgress() {
|
||||
progressCircle.setProgress(progress);
|
||||
progressText.setText(progress + "/" + maxProgress);
|
||||
}
|
||||
|
||||
public void triggerColorBurst(boolean isGoodRep) {
|
||||
|
||||
Log.i("InputProcessor", "Color Burst");
|
||||
|
||||
try {
|
||||
int circleId = isGoodRep ? R.drawable.progress_circle_good : R.drawable.progress_circle_bad;
|
||||
int soundId = isGoodRep ? R.raw.good_sound : R.raw.wrong_sound;
|
||||
progressCircle.setProgressDrawable(ContextCompat.getDrawable(this, circleId));
|
||||
// MediaPlayer.create(this, soundId).start();
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(progressCircle, "alpha", 1f, 0f, 1f);
|
||||
animator.setDuration(700); // Burst duration
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
progressCircle.setProgressDrawable(ContextCompat.getDrawable(FitnessActivity.this, R.drawable.progress_circle));
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +1,11 @@
|
||||
package com.example.fitbot.ui.activities;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.example.fitbot.R;
|
||||
import com.example.fitbot.util.ButtonNavigation;
|
||||
import com.example.fitbot.pepper.Pepper;
|
||||
import com.example.fitbot.util.NavigationManager;
|
||||
|
||||
public class HelpActivity extends AppCompatActivity {
|
||||
|
||||
@@ -14,18 +14,12 @@ public class HelpActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_help);
|
||||
|
||||
ButtonNavigation.setupButtonNavigation(this, R.id.homeButtonHelp, MainActivity.class);
|
||||
Pepper.say("Welkom bij FitBot. De robot die helpt om fit te blijven. Druk op Start om de oefening te beginnen. Ga terug naar het begin scherm door op het huisje te klikken");
|
||||
|
||||
// Hide system UI
|
||||
hideSystemUI();
|
||||
}
|
||||
|
||||
private void hideSystemUI() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
// Hide the status bar and navigation bar
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
|
||||
NavigationManager.setupButtonNavigation(this, R.id.homeButtonHelp, MainActivity.class);
|
||||
|
||||
NavigationManager.hideSystemUI(this);
|
||||
}
|
||||
}
|
@@ -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,13 +11,17 @@ 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.pepper.Pepper;
|
||||
import com.example.fitbot.util.NavigationManager;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@@ -31,7 +36,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
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);
|
||||
@@ -49,7 +53,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
setUpUi();
|
||||
|
||||
// Hide system UI
|
||||
hideSystemUI();
|
||||
NavigationManager.hideSystemUI(this);
|
||||
sendIpAddress(this);
|
||||
}
|
||||
|
||||
private void setUpUi() {
|
||||
@@ -63,8 +68,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().hide();
|
||||
}
|
||||
ButtonNavigation.setupButtonNavigation(this, R.id.startButtonMain, FitnessActivity.class);
|
||||
ButtonNavigation.setupButtonNavigation(this, R.id.helpButtonMain, HelpActivity.class);
|
||||
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
|
||||
@@ -80,33 +85,22 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
super.onDrawerOpened(drawerView);
|
||||
hideSystemUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
super.onDrawerClosed(drawerView);
|
||||
hideSystemUI();
|
||||
}
|
||||
};
|
||||
drawerLayout.addDrawerListener(toggle);
|
||||
toggle.syncState(); // Synchronize the state of the navigation drawer
|
||||
}
|
||||
|
||||
private void hideSystemUI() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
// Hide the status bar and navigation bar
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) {
|
||||
hideSystemUI();
|
||||
NavigationManager.hideSystemUI(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,4 +112,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,254 +0,0 @@
|
||||
package com.example.fitbot.ui.components;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.aldebaran.qi.sdk.QiContext;
|
||||
import com.example.fitbot.exercise.Exercise;
|
||||
import com.example.fitbot.ui.activities.EndScreenActivity;
|
||||
import com.example.fitbot.ui.activities.FitnessActivity;
|
||||
import com.example.fitbot.ui.activities.MainActivity;
|
||||
import com.example.fitbot.util.FitnessCycle;
|
||||
import com.example.fitbot.util.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 PersonalMotionPreviewElement extends View implements IInputHandler {
|
||||
|
||||
// Fields regarding Exercise and speech handling.
|
||||
private InputProcessor motionProcessor;
|
||||
private Exercise exercise;
|
||||
private QiContext qiContext;
|
||||
private int exerciseCount;
|
||||
|
||||
private FitnessActivity parentActivity;
|
||||
|
||||
private final Paint userProgressPaint = new Paint();
|
||||
private final Paint borderPaint = new Paint();
|
||||
private final Paint backgroundPaint = new Paint();
|
||||
|
||||
// TODO: Remove
|
||||
private Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation.
|
||||
private Matrix4f projectionMatrix = new Matrix4f(); // The projection matrix for the 3D to 2D transformation.
|
||||
private final Vector4f objectPosition = new Vector4f(0, 0, 0, 1); // The location of the object in 3D space.
|
||||
private ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
|
||||
|
||||
// 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 PersonalMotionPreviewElement(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())
|
||||
};
|
||||
}
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
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) {
|
||||
this.qiContext = context;
|
||||
|
||||
// Handler that is called every time the motion processor receives new data.
|
||||
this.motionProcessor.setInputHandler((rotationVector, deviceId) -> {
|
||||
|
||||
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
|
||||
Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed()));
|
||||
|
||||
if ( this.motionProcessor.hasFinished())
|
||||
{
|
||||
if ( this.parentActivity == null)
|
||||
{
|
||||
// Move to main screen
|
||||
this.destroy();
|
||||
Intent intent = new Intent(getContext(), MainActivity.class);
|
||||
getContext().startActivity(intent);
|
||||
return;
|
||||
}
|
||||
// Move on to the next exercise, or finish.
|
||||
if ( this.exerciseCount > 0 )
|
||||
{
|
||||
this.exerciseCount--;
|
||||
this.exercise = this.parentActivity.acquireExercise();
|
||||
this.motionProcessor.useExercise(this.exercise);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Finish the exercise.
|
||||
this.destroy();
|
||||
Intent intent = new Intent(getContext(), EndScreenActivity.class);
|
||||
getContext().startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Adjust / remove
|
||||
vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight()));
|
||||
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
|
||||
Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight());
|
||||
|
||||
this.vectors.add(parsed);
|
||||
// Remove the first element if the array is too big
|
||||
if (this.vectors.size() > 100)
|
||||
this.vectors.poll();
|
||||
});
|
||||
|
||||
if (this.qiContext == null)
|
||||
return;
|
||||
|
||||
FitnessCycle.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)], this.qiContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for setting the gesture path that will be drawn on the canvas.
|
||||
*
|
||||
* @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;*/
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
@@ -11,8 +11,7 @@ import com.aldebaran.qi.sdk.object.actuation.Animation;
|
||||
|
||||
public class Animations {
|
||||
|
||||
public static void Animate(String AnimationFile, QiContext ctx)
|
||||
{
|
||||
public static void Animate(String AnimationFile, QiContext ctx) {
|
||||
int resId = ctx.getResources().getIdentifier(AnimationFile, "raw", ctx.getPackageName());
|
||||
|
||||
Animation animation = AnimationBuilder.with(ctx)
|
||||
|
@@ -1,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();
|
||||
});
|
||||
}
|
||||
}
|
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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<Void, Void, Void>() {
|
||||
@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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package com.example.fitbot.util.path;
|
||||
|
||||
import com.example.fitbot.exercise.ExerciseManager;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public class AnglePath {
|
||||
|
||||
// The angles
|
||||
private final Vector3f[] angles;
|
||||
|
||||
/**
|
||||
* Create a new gesture path with a given set of vectors and curvature.
|
||||
*
|
||||
* @param angles The vectors that make up the path.
|
||||
*/
|
||||
public AnglePath(Vector3f[] angles) {
|
||||
this.angles = angles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for retrieving the vectors of the GesturePath.
|
||||
*/
|
||||
public Vector3f[] getAngleVectors() {
|
||||
return this.angles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for converting a string to a GesturePath object.
|
||||
* This function has been updated to convert Json strings to AnglePath objects.
|
||||
* The JSON must be in the following format:
|
||||
* [
|
||||
* { "deviceId": number, "data": [ [x, y, z], [x, y, z], ... ] },
|
||||
* ]
|
||||
*
|
||||
* @param jsonInput The string to convert
|
||||
* @return The AnglePath object
|
||||
*/
|
||||
|
||||
public static AnglePath[] fromString(String jsonInput) {
|
||||
if (jsonInput == null)
|
||||
throw new IllegalArgumentException("Input string cannot be null");
|
||||
|
||||
JsonElement parsed = JsonParser.parseString(jsonInput);
|
||||
if (!parsed.isJsonArray())
|
||||
throw new IllegalArgumentException("Input string must be a JSON array");
|
||||
|
||||
if ( parsed.getAsJsonArray().size() != ExerciseManager.SENSOR_COUNT)
|
||||
throw new IllegalArgumentException("Input string must contain 2 elements");
|
||||
|
||||
Vector3f[][] angles = new Vector3f[ExerciseManager.SENSOR_COUNT][];
|
||||
|
||||
for ( int dataArrayIdx = 0; dataArrayIdx < parsed.getAsJsonArray().size(); dataArrayIdx++)
|
||||
{
|
||||
JsonArray array = parsed.getAsJsonArray().get(dataArrayIdx).getAsJsonObject().get("data").getAsJsonArray();
|
||||
int deviceIdx = parsed.getAsJsonArray().get(dataArrayIdx).getAsJsonObject().get("deviceId").getAsInt();
|
||||
angles[deviceIdx] = new Vector3f[array.size()];
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JsonArray vec = array.get(i).getAsJsonArray();
|
||||
angles[deviceIdx][i] = new Vector3f(vec.get(0).getAsFloat(), vec.get(1).getAsFloat(), vec.get(2).getAsFloat());
|
||||
}
|
||||
}
|
||||
return new AnglePath[] {new AnglePath(angles[0]), new AnglePath(angles[1])};
|
||||
}
|
||||
}
|
@@ -1,123 +0,0 @@
|
||||
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.
|
||||
private final PathSegment[] segments;
|
||||
|
||||
/**
|
||||
* Create a new gesture path with a given set of vectors and curvature.
|
||||
*
|
||||
* @param vectors The vectors that make up the path.
|
||||
*/
|
||||
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++)
|
||||
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) {
|
||||
this.segments = segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter method for retrieving the path segments of this GesturePath.
|
||||
*
|
||||
* @return The path segments.
|
||||
*/
|
||||
public PathSegment[] getSegments() {
|
||||
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.
|
||||
*
|
||||
* @param reference The reference point to find the closest path segment to.
|
||||
* @return The closest path segment to the reference point.
|
||||
*/
|
||||
public PathSegment closest(Vector3f reference) {
|
||||
// If there's only one segment, return that one.
|
||||
if ( segments.length == 1)
|
||||
return segments[0];
|
||||
|
||||
PathSegment closest = segments[0];
|
||||
for ( int i = 1; i < segments.length; i++)
|
||||
closest = PathSegment.closer(closest, segments[i], reference);
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error between an arbitrary path segment and the reference point.
|
||||
*
|
||||
* @param referencePoint The reference point to calculate the error of.
|
||||
* @return The error offset between the path and the reference point.
|
||||
*/
|
||||
public double getError(Vector3f referencePoint) {
|
||||
return closest(referencePoint).difference(referencePoint); // Get the closest segment and calculate the error.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function for converting a string to a GesturePath object.
|
||||
* The input string bytes will be directly converted into 3d vectors.
|
||||
* Every scalar is composed of 32 bits (4 characters), meaning 96 bits per vector.
|
||||
*
|
||||
* Note: ASCII to Vector conversion is done in Big Endian format (most significant byte first).
|
||||
*
|
||||
* @param input The string to convert
|
||||
* @return The GesturePath object
|
||||
*/
|
||||
|
||||
public static GesturePath fromString(String input) {
|
||||
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];
|
||||
|
||||
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]);
|
||||
}
|
||||
return new GesturePath(vectors);
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ public class PathSegment {
|
||||
* pointing straight upwards relative to the line, with a curvature of 0.0.
|
||||
*
|
||||
* @param start The starting point of the line segment
|
||||
* @param end The end point of the line segment.
|
||||
* @param end The end point of the line segment.
|
||||
*/
|
||||
public PathSegment(Vector3f start, Vector3f end) {
|
||||
this.start = start;
|
||||
@@ -25,7 +25,7 @@ public class PathSegment {
|
||||
* depending on the curvature of the curve. If the curvature is unset, or set to 0
|
||||
* then this method will use linear interpolation. Otherwise, it will use a curve.
|
||||
*
|
||||
* @param t The interpolation value between 0 and 1.
|
||||
* @param t The interpolation value between 0 and 1.
|
||||
*/
|
||||
public Vector3f interpolate(double t) {
|
||||
return new Vector3f(this.start)
|
||||
@@ -56,7 +56,7 @@ public class PathSegment {
|
||||
(float) (this.start.x + t * (this.end.x - this.start.x)),
|
||||
(float) (this.start.y + t * (this.end.y - this.start.y)),
|
||||
(float) (this.start.z + t * (this.end.z - this.start.z))
|
||||
));
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,7 @@ public class PathSegment {
|
||||
* @return The distance to the closest point on the path segment.
|
||||
*/
|
||||
public double distance(Vector3f reference) {
|
||||
if ( this.start.distanceSquared(reference) > this.end.distanceSquared(reference))
|
||||
if (this.start.distanceSquared(reference) > this.end.distanceSquared(reference))
|
||||
return this.end.distance(reference);
|
||||
return this.start.distance(reference);
|
||||
}
|
||||
@@ -92,8 +92,8 @@ public class PathSegment {
|
||||
/**
|
||||
* Function for returning the closest path segment to a reference point.
|
||||
*
|
||||
* @param first The first path segment to compare.
|
||||
* @param second The second path segment to compare.
|
||||
* @param first The first path segment to compare.
|
||||
* @param second The second path segment to compare.
|
||||
* @param referencePoint The reference point to compare to.
|
||||
* @return The closest path segment to the reference point.
|
||||
*/
|
||||
|
@@ -4,11 +4,12 @@ import org.joml.Vector3f;
|
||||
|
||||
public interface IInputHandler {
|
||||
|
||||
/**
|
||||
* Function for accepting motion data and the transformed vector.
|
||||
* @param rotationVector The rotation vector of the motion data.
|
||||
* @param sensorId The sensor ID of the motion data.
|
||||
*/
|
||||
void accept(Vector3f rotationVector, int sensorId);
|
||||
/**
|
||||
* Function for accepting motion data and the transformed vector.
|
||||
*
|
||||
* @param rotationVector The rotation vector of the motion data.
|
||||
* @param sensorId The sensor ID of the motion data.
|
||||
*/
|
||||
void accept(Vector3f rotationVector, int sensorId);
|
||||
|
||||
}
|
@@ -1,70 +1,232 @@
|
||||
package com.example.fitbot.util.processing;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.fitbot.exercise.Exercise;
|
||||
import com.example.fitbot.exercise.ExerciseManager;
|
||||
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.server.WebServer;
|
||||
import com.google.gson.JsonArray;
|
||||
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 org.json.JSONArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InputProcessor {
|
||||
|
||||
private Vector3f[][] selfRotationVectorPaths; // Relative path of the motion data
|
||||
private List<Vector3f>[] selfRotationVectorPaths = null; // Relative path of the motion data
|
||||
private Vector3f[][] targetRotationVectorPaths; // Target path of the motion data
|
||||
|
||||
private final float sampleRate; // The sample rate of the motion sensor
|
||||
private float exerciseDuration;
|
||||
private float exerciseRepetitionDurationInSeconds = 0.0f;
|
||||
private int exercisesRemaining = 1;
|
||||
|
||||
private float errorCheckInterval_s;
|
||||
private int checksPerformed = 0;
|
||||
private int totalChecks = 0;
|
||||
|
||||
private final FitnessActivity parentActivity;
|
||||
|
||||
/**
|
||||
* The phrases that are said by the robot whenever the exercise starts.
|
||||
*/
|
||||
private static final String[] STARTING_PHRASES = {
|
||||
"Veel success met de oefening!",
|
||||
"Je kan het!",
|
||||
"Veel plezier!"
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = (rotationVector, deviceId) -> {
|
||||
};
|
||||
private static final String[] REQUIRED_SENSOR_JSON_PROPERTIES =
|
||||
{"rotationX", "rotationY", "rotationZ", "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.exerciseDuration = exerciseTime;
|
||||
public InputProcessor(FitnessActivity parentActivity) {
|
||||
this.parentActivity = parentActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for setting the exercise to use.
|
||||
* This updates the user and target path and the
|
||||
* duration of the exercise.
|
||||
* <p>
|
||||
* This function is only initially used to select the starting exercise;
|
||||
* the exercises that follow are determined by a private method 'nextExercise'
|
||||
*
|
||||
* @param exercise The exercise to use the paths for.
|
||||
*/
|
||||
public void useExercise(Exercise exercise) {
|
||||
this.selfRotationVectorPaths = new Vector3f[2][(int) (exercise.exerciseTimeInSeconds * this.sampleRate)];
|
||||
this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getVectors().length];
|
||||
this.exerciseDuration = exercise.exerciseTimeInSeconds;
|
||||
this.secondsPassed = 0.0D;
|
||||
this.lastTime = System.currentTimeMillis();
|
||||
if (this.recordingMovement)
|
||||
throw new IllegalStateException("Cannot change exercise while recording movement.");
|
||||
|
||||
this.exercisesRemaining = 1;
|
||||
this.nextExercise(exercise);
|
||||
Pepper.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for checking if the exercise has finished.
|
||||
* @return True if the exercise has finished, false otherwise.
|
||||
* Function that starts checking for user movement.
|
||||
* This function will start a thread that will check for user movement
|
||||
* and compare the last rotation vectors to the target rotation vectors.
|
||||
*/
|
||||
public void startCheckingUserMovement() {
|
||||
// Error checking thread.
|
||||
(new Thread(() -> {
|
||||
Log.i("InputProcessor", "Movement Checking Thread started");
|
||||
|
||||
while (this.exercisesRemaining > 0) {
|
||||
|
||||
if ( this.totalChecks == 0 || this.selfRotationVectorPaths == null
|
||||
|| this.selfRotationVectorPaths.length == 0
|
||||
|| this.selfRotationVectorPaths[0].size() == 0
|
||||
|| this.selfRotationVectorPaths[1].size() == 0)
|
||||
continue;
|
||||
|
||||
boolean isFaulty = this.isFaultyMovement();
|
||||
|
||||
Log.i("InputProcessor", "Movement checked: " + (isFaulty ? "Faulty" : "Good"));
|
||||
|
||||
if (isFaulty) {
|
||||
this.onInadequateRepetition();
|
||||
} else this.onAdequateRepetition();
|
||||
|
||||
this.checksPerformed++;
|
||||
|
||||
if (this.checksPerformed >= this.totalChecks)
|
||||
{
|
||||
this.checksPerformed = 0;
|
||||
this.exercisesRemaining--;
|
||||
acquireExercise();
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep((long) (this.errorCheckInterval_s * 1000));
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
})).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves on to the next exercise without changing the remaining exercises.
|
||||
*
|
||||
* @param exercise The exercise to move on to.
|
||||
*/
|
||||
private void nextExercise(Exercise exercise) {
|
||||
//TODO: Remove when more than one exercise
|
||||
if (this.exercisesRemaining <= 0) {
|
||||
Log.i("InputProcessor", "Moving to end screen; finished all exercises");
|
||||
// Move to end screen on main activity
|
||||
this.parentActivity.runOnUiThread(() -> NavigationManager.navigateToActivity(this.parentActivity, EndScreenActivity.class));
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("InputProcessor", "Acquired next exercise: " + exercise.name);
|
||||
ExerciseManager.TOTAL_REPETITIONS_REQUIRED += ExerciseManager.DEFAULT_EXERCISE_REPETITIONS;
|
||||
ExerciseManager.TOTAL_EXERCISES_PREFORMED++;
|
||||
|
||||
this.checksPerformed = 0;
|
||||
this.totalChecks = ExerciseManager.DEFAULT_EXERCISE_REPETITIONS * 6;
|
||||
|
||||
this.selfRotationVectorPaths = new ArrayList[2];
|
||||
this.selfRotationVectorPaths[0] = new ArrayList<>();
|
||||
this.selfRotationVectorPaths[1] = new ArrayList<>();
|
||||
|
||||
this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getAngleVectors().length];
|
||||
this.targetRotationVectorPaths[0] = exercise.leftPath.getAngleVectors();
|
||||
this.targetRotationVectorPaths[1] = exercise.rightPath.getAngleVectors();
|
||||
this.exerciseRepetitionDurationInSeconds = exercise.exerciseTimeInSeconds;
|
||||
this.secondsPassed = 0.0D;
|
||||
this.lastTime = System.currentTimeMillis();
|
||||
|
||||
Log.i("InputProcessor", "Repetition time: " + exercise.exerciseTimeInSeconds);
|
||||
this.exerciseRepetitionDurationInSeconds = Math.max(this.exerciseRepetitionDurationInSeconds, 2);
|
||||
this.errorCheckInterval_s = exercise.exerciseTimeInSeconds / 3.0f;
|
||||
Log.i("InputProcessor", "Exercise error checking interval: " + this.errorCheckInterval_s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called whenever the user performs a good repetition.
|
||||
*/
|
||||
public void onAdequateRepetition() {
|
||||
ExerciseManager.TOTAL_REPETITIONS_PERFORMED++;
|
||||
Log.i("InputProcessor", "Adequate repetition performed");
|
||||
this.parentActivity.runOnUiThread(this.parentActivity::incrementProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called whenever the user performs a bad repetition.
|
||||
*/
|
||||
public void onInadequateRepetition() {
|
||||
Log.i("InputProcessor", "Inadequate repetition performed");
|
||||
this.parentActivity.runOnUiThread(() -> this.parentActivity.triggerColorBurst(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
this.selfRotationVectorPaths[0] = new ArrayList<>();
|
||||
this.selfRotationVectorPaths[1] = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.secondsPassed >= this.exerciseDuration;
|
||||
return this.recordingMovement ?
|
||||
(this.secondsPassed >= this.recordingDurationInSeconds) :
|
||||
(this.secondsPassed >= this.exerciseRepetitionDurationInSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +250,30 @@ public class InputProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for acquiring the next exercise from the database.
|
||||
* Upon successful retrieval, it will call the nextExercise method.
|
||||
*/
|
||||
private void acquireExercise() {
|
||||
|
||||
if ( this.exercisesRemaining <= 0)
|
||||
{
|
||||
Log.i("InputProcessor", "Exercises finished");
|
||||
this.parentActivity.runOnUiThread(() -> {
|
||||
NavigationManager.navigateToActivity(this.parentActivity, EndScreenActivity.class);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("MotionProcessor", "Fetching exercise data.");
|
||||
|
||||
this.parentActivity.fetchExerciseAsync(this::nextExercise, (nil) -> {
|
||||
|
||||
Log.i("MotionProcessor", "Failed to fetch exercise data.");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for stopping the listening process
|
||||
* of the motion sensor. This function will stop
|
||||
@@ -109,6 +295,9 @@ public class InputProcessor {
|
||||
|
||||
try {
|
||||
|
||||
Log.i("MotionProcessor", "Time passed: " + this.secondsPassed + "s");
|
||||
if (this.recordingMovement)
|
||||
Log.i("MotionProcessor", this.secondsPassed + " / " + this.recordingDurationInSeconds);
|
||||
Log.i("MotionProcessor", "Received packet data: " + data);
|
||||
|
||||
JsonElement json = JsonParser.parseString(data);
|
||||
@@ -118,23 +307,20 @@ public class InputProcessor {
|
||||
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
|
||||
String[] required = {
|
||||
"rotationX", "rotationY", "rotationZ",
|
||||
"type",
|
||||
"deviceId"
|
||||
};
|
||||
|
||||
// Ensure all properties are present in the received JSON object
|
||||
for (String s : required) {
|
||||
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());
|
||||
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();
|
||||
|
||||
// Parse the retrieved data
|
||||
parseRotationVector(rotation, deviceId);
|
||||
} catch (Exception e) {
|
||||
Log.i("MotionProcessor", "Failed to parse packet data.");
|
||||
@@ -152,85 +338,112 @@ public class InputProcessor {
|
||||
|
||||
// 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);
|
||||
this.selfRotationVectorPaths[deviceId].add(rotation);
|
||||
|
||||
motionDataConsumer.accept(rotation, deviceId);
|
||||
if (selfIndex >= selfRotationVectorPaths[deviceId].length || selfIndex < 0)
|
||||
return;
|
||||
if (this.recordingMovement && this.secondsPassed >= this.recordingDurationInSeconds) {
|
||||
// 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.
|
||||
|
||||
selfRotationVectorPaths[deviceId][selfIndex] = rotation;
|
||||
String converted = convertRecordedDataToString();
|
||||
//split string into 4 to print it fully in console
|
||||
final int quarter = converted.length() / 4;
|
||||
String[] parts = {
|
||||
converted.substring(0, quarter),
|
||||
converted.substring(quarter, 2 * quarter),
|
||||
converted.substring(2 * quarter, 3 * quarter),
|
||||
converted.substring(3 * quarter)
|
||||
};
|
||||
Log.i("MotionProcessor", "Converted data: ");
|
||||
Log.i("ProcessedData", parts[0]);
|
||||
Log.i("ProcessedData", parts[1]);
|
||||
Log.i("ProcessedData", parts[2]);
|
||||
Log.i("ProcessedData", parts[3]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Do something else with the vector
|
||||
Log.i("MotionProcessor", "Rotation vector: " + rotation.toString() + " from device: " + deviceId);
|
||||
|
||||
// Whenever the exercise has finished and it's not recording,
|
||||
// attempt to move to the next exercise.
|
||||
// If this fails, navigate back to the main activity.
|
||||
if (this.hasFinished() && !this.recordingMovement)
|
||||
acquireExercise();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for getting the current progress of the exercise.
|
||||
* The return value will range between 0.0 and 1.0.
|
||||
* 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 current progress of the exercise.
|
||||
* @return The converted string.
|
||||
*/
|
||||
public double getCurrentProgress()
|
||||
{
|
||||
return secondsPassed / exerciseDuration;
|
||||
}
|
||||
private String convertRecordedDataToString() {
|
||||
int[] intBits = new int[3];
|
||||
char[] vectorChars = new char[12]; // 4 bytes per scalar, 12 chars per vector
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
/*
|
||||
* Convert to JSON array in the following format:
|
||||
* [ { "deviceId": number, "data": [ [x, y, z], [x, y, z], ... ] }]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function for setting the motion data receiver.
|
||||
*
|
||||
* @param consumer The consumer to set.
|
||||
*/
|
||||
public void setInputHandler(IInputHandler consumer) {
|
||||
if (consumer != null)
|
||||
this.motionDataConsumer = consumer;
|
||||
}
|
||||
// Iterate over all devices. In the current instance, it's 2.
|
||||
for (int deviceId = 0; deviceId < selfRotationVectorPaths.length; deviceId++) {
|
||||
JsonObject jsonDeviceObject = new JsonObject();
|
||||
jsonDeviceObject.addProperty("deviceId", deviceId);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// Data array
|
||||
JsonArray jsonDeviceDataArray = new JsonArray();
|
||||
|
||||
// Ensure the sensor ID is within the bounds of the array
|
||||
if (sensorId < 0 || sensorId >= selfRotationVectorPaths.length)
|
||||
return 0.0d;
|
||||
for (Vector3f vector : selfRotationVectorPaths[deviceId]) {
|
||||
JsonArray jsonScalarArray = new JsonArray();
|
||||
jsonScalarArray.add(vector.x);
|
||||
jsonScalarArray.add(vector.y);
|
||||
jsonScalarArray.add(vector.z);
|
||||
jsonDeviceDataArray.add(jsonScalarArray);
|
||||
}
|
||||
jsonDeviceObject.add("data", jsonDeviceDataArray);
|
||||
|
||||
// Index of the current rotation vector
|
||||
int targetIndex = (int) ((this.exerciseDuration / this.targetRotationVectorPaths[sensorId].length) * time);
|
||||
int selfIndex = (int) (this.selfRotationVectorPaths[sensorId].length / this.sampleRate * time);
|
||||
|
||||
// Ensure the indexes are within the bounds of the array
|
||||
if (targetIndex >= 0 && targetIndex <= this.targetRotationVectorPaths[sensorId].length - 1 &&
|
||||
selfIndex >= 0 && selfIndex <= this.selfRotationVectorPaths[sensorId].length - 1)
|
||||
{
|
||||
return this.selfRotationVectorPaths[sensorId][selfIndex].distance(this.targetRotationVectorPaths[sensorId][targetIndex]);
|
||||
jsonArray.add(jsonDeviceObject);
|
||||
}
|
||||
return 0.0d;
|
||||
return jsonArray.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Function for checking whether the last movement was faulty
|
||||
*/
|
||||
public double getAverageError(int sensorId) {
|
||||
double error = 0;
|
||||
for (int i = 0; i < this.exerciseDuration; i++) {
|
||||
error += getError(sensorId, i);
|
||||
}
|
||||
return error / this.exerciseDuration;
|
||||
}
|
||||
public boolean isFaultyMovement() {
|
||||
boolean upMovementDetected = false;
|
||||
boolean downMovementDetected = false;
|
||||
|
||||
public float secondsPassed() {
|
||||
return (float) secondsPassed;
|
||||
}
|
||||
}
|
||||
for (List<Vector3f> path : selfRotationVectorPaths) {
|
||||
if (path.size() < 2) {
|
||||
continue; // Skip if there are not enough points to compare
|
||||
}
|
||||
|
||||
Vector3f firstPoint = path.get(0);
|
||||
Vector3f lastPoint = path.get(path.size() - 1);
|
||||
|
||||
float y1 = firstPoint.y;
|
||||
float y2 = lastPoint.y;
|
||||
|
||||
if (y2 > y1) {
|
||||
upMovementDetected = true;
|
||||
} else if (y2 < y1) {
|
||||
downMovementDetected = true;
|
||||
}
|
||||
|
||||
if (upMovementDetected && downMovementDetected) {
|
||||
return false; // Return false for faulty movement if both up and down movements are detected
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Return true for faulty movement if only up or down movement is detected
|
||||
}}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package com.example.fitbot.util.server;
|
||||
|
||||
import java.net.Socket;
|
||||
|
||||
/**
|
||||
* Interface for handling WebSocket events.
|
||||
*/
|
||||
|
@@ -10,16 +10,13 @@ import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class WebServer implements Runnable {
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
protected IWebServerHandler eventHandler = (input) -> {}; // No-op.
|
||||
protected IWebServerHandler eventHandler = (input) -> {
|
||||
}; // No-op.
|
||||
|
||||
private Thread thread;
|
||||
private final AtomicBoolean forceClose = new AtomicBoolean(false);
|
||||
@@ -84,7 +81,7 @@ public class WebServer implements Runnable {
|
||||
|
||||
String[] data = builder.toString().split("\n\n");
|
||||
|
||||
if ( data.length > 1) { // Check if the data is valid.
|
||||
if (data.length > 1) { // Check if the data is valid.
|
||||
this.eventHandler.onReceive(data[1]);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/lightBlue" />
|
||||
<stroke android:width="2dp" android:color="#FF0000" />
|
||||
<stroke android:width="2dp" android:color="#FFFFFF" />
|
||||
<corners android:radius="20dp" />
|
||||
</shape>
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/midBlue" />
|
||||
<stroke android:width="2dp" android:color="#FF0000" />
|
||||
<stroke android:width="2dp" android:color="#FFFFFF" />
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/midderBlue" />
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/midBlue" />
|
||||
<stroke android:width="2dp" android:color="#FFFFFF" />
|
||||
<corners android:radius="360dp" />
|
||||
</shape>
|
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
@@ -0,0 +1,5 @@
|
||||
<vector android:height="48dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
@@ -0,0 +1,5 @@
|
||||
<vector android:height="40dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
@@ -0,0 +1,5 @@
|
||||
<vector android:height="48dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
@@ -0,0 +1,5 @@
|
||||
<vector android:height="48dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
|
||||
</vector>
|
@@ -1,170 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF0000"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#D10000"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/progress">
|
||||
<rotate
|
||||
android:fromDegrees="270"
|
||||
android:toDegrees="270">
|
||||
<shape android:shape="ring">
|
||||
<gradient
|
||||
android:startColor="#ADD8E6"
|
||||
android:endColor="#798994"
|
||||
android:angle="0"
|
||||
android:type="sweep"/>
|
||||
<size android:width="150dp"
|
||||
android:height="150dp"/>
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
</layer-list>
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/progress">
|
||||
<rotate
|
||||
android:fromDegrees="270"
|
||||
android:toDegrees="270">
|
||||
<shape android:shape="ring">
|
||||
<gradient
|
||||
android:startColor="#FF0000"
|
||||
android:endColor="#FF0000"
|
||||
android:angle="0"
|
||||
android:type="sweep"/>
|
||||
<size android:width="150dp"
|
||||
android:height="150dp"/>
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
</layer-list>
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/progress">
|
||||
<rotate
|
||||
android:fromDegrees="270"
|
||||
android:toDegrees="270">
|
||||
<shape android:shape="ring">
|
||||
<gradient
|
||||
android:startColor="#00FF00"
|
||||
android:endColor="#00FF00"
|
||||
android:angle="0"
|
||||
android:type="sweep"/>
|
||||
<size android:width="150dp"
|
||||
android:height="150dp"/>
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
</layer-list>
|
@@ -2,7 +2,7 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners
|
||||
android:radius="25dp"
|
||||
android:radius="30dp"
|
||||
/>
|
||||
|
||||
<gradient
|
||||
|
@@ -8,13 +8,14 @@
|
||||
tools:context=".ui.activities.HelpActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="800dp"
|
||||
android:layout_height="450dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="80dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/help2_background"
|
||||
android:layout_marginTop="80dp"
|
||||
android:background="@drawable/border_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:padding="40dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@@ -24,10 +25,10 @@
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="15dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@@ -40,11 +41,11 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="700dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -56,27 +57,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/voltooid"
|
||||
android:textAlignment="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="700dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewScoreEndScreen"
|
||||
style="@style/TextStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/score"
|
||||
android:textAlignment="center"/>
|
||||
android:textAlignment="center" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -85,8 +66,8 @@
|
||||
android:id="@+id/homeButtonEndScreen"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="75dp"
|
||||
android:layout_marginEnd="280dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:layout_marginEnd="404dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:drawableTop="@drawable/ic_baseline_home_48"
|
||||
android:drawableTint="@color/white"
|
||||
@@ -94,20 +75,4 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/startButtonEndScreen"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="75dp"
|
||||
android:layout_marginStart="280dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:padding="15dp"
|
||||
android:text="@string/start"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="30sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
@@ -1,42 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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">
|
||||
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">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="900dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="450dp"
|
||||
android:layout_height="450dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:padding="15dp"
|
||||
android:background="@drawable/help2_background"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<VideoView
|
||||
android:id="@+id/videoView"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="400dp"
|
||||
android:layout_marginStart="20dp"
|
||||
/>
|
||||
<android.support.v7.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerInParent="true"
|
||||
app:cardCornerRadius="20dp">
|
||||
|
||||
<com.example.fitbot.ui.components.PersonalMotionPreviewElement
|
||||
android:id="@+id/personalMotionPreviewElement"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="400dp"
|
||||
android:layout_marginStart="20dp"
|
||||
/>
|
||||
<VideoView
|
||||
android:id="@+id/videoView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/loadingPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loadingCircle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:progressTint="@color/white"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/infoButtonFitness"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:drawableTop="@drawable/ic_baseline_info_40"
|
||||
android:drawableTint="@color/white"
|
||||
android:padding="5dp"
|
||||
tools:ignore="SpeakableTextPresentCheck" />
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="450dp"
|
||||
android:layout_height="450dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@drawable/border_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewFitnessTitle"
|
||||
style="@style/TextStyleTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/exerciseTitle"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_margin="10dp"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:padding="5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewFitnessShortDescription"
|
||||
style="@style/TextStyleDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/exerciseDesc"
|
||||
android:textAlignment="center" />
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_margin="10dp"
|
||||
android:background="@drawable/border_background_circle">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressCircle"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="false"
|
||||
android:max="10"
|
||||
android:progress="1"
|
||||
android:progressDrawable="@drawable/progress_circle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressText"
|
||||
style="@style/TextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="1/10" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -44,7 +132,7 @@ tools:openDrawer="start">
|
||||
android:id="@+id/homeButtonFitness"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="75dp"
|
||||
android:layout_marginStart="404dp"
|
||||
android:layout_marginStart="175dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:drawableTop="@drawable/ic_baseline_home_48"
|
||||
@@ -54,4 +142,18 @@ tools:openDrawer="start">
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:ignore="SpeakableTextPresentCheck" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/skipButtonFitness"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="75dp"
|
||||
android:layout_marginEnd="175dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:drawableTop="@drawable/ic_baseline_skip_next_48"
|
||||
android:drawableTint="@color/white"
|
||||
android:padding="15dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:ignore="SpeakableTextPresentCheck" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
@@ -28,7 +28,7 @@
|
||||
android:layout_height="450dp"
|
||||
android:layout_marginStart="80dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/help2_background"
|
||||
android:background="@drawable/border_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -40,10 +40,9 @@
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:padding="16dp"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@@ -56,11 +55,11 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="700dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -76,11 +75,11 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="700dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/help_background"
|
||||
android:background="@drawable/border_background_3"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
45
code/src/Fitbot/app/src/main/res/layout/dialog_info.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="800dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/red_button_gradient"
|
||||
android:orientation="vertical"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewTitleDialog"
|
||||
style="@style/TextStyleTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/descriptionTitle" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/closeButtonDialog"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="@color/transparent"
|
||||
android:drawableTop="@drawable/ic_baseline_close_48"
|
||||
android:drawableTint="@color/white" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewDialogDescription"
|
||||
style="@style/TextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="800dp"
|
||||
android:text="@string/description"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
</LinearLayout>
|
@@ -15,6 +15,7 @@
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:src="@drawable/robot_logo"
|
||||
android:tint="@color/white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
android:layout_marginTop="60dp"
|
||||
android:text="FitBot"
|
||||
android:textSize="48sp"
|
||||
android:textColor="@color/darkBlue"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/fitbot_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/fitbot_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/fitbot_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/fitbot_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
</adaptive-icon>
|
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
</adaptive-icon>
|
BIN
code/src/Fitbot/app/src/main/res/mipmap-hdpi/fitbot_launcher.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 6.6 KiB |
BIN
code/src/Fitbot/app/src/main/res/mipmap-mdpi/fitbot_launcher.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 26 KiB |
BIN
code/src/Fitbot/app/src/main/res/raw/arm_raises.mp4
Normal file
275
code/src/Fitbot/app/src/main/res/raw/armcircle.qianim
Normal file
@@ -0,0 +1,275 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Animation xmlns:editor="http://www.ald.softbankrobotics.com/animation/editor" typeVersion="2.0" editor:fps="25">
|
||||
<ActuatorCurve fps="25" actuator="HeadYaw" mute="false" unit="degree">
|
||||
<Key value="-0.439454377" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HeadPitch" mute="false" unit="degree">
|
||||
<Key value="-12.1289139" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="-119.500008" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="119.500008" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-119.500008" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="119.500008" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="6.67969275" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-69.7851486" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowRoll" mute="false" unit="degree">
|
||||
<Key value="-29.7070332" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-29.7070332" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-29.7070332" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-29.7070332" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LWristYaw" mute="false" unit="degree">
|
||||
<Key value="-1.76023543" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.589630966" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipRoll" mute="false" unit="degree">
|
||||
<Key value="-0.26367262" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipPitch" mute="false" unit="degree">
|
||||
<Key value="-1.49414492" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="KneePitch" mute="false" unit="degree">
|
||||
<Key value="0" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="-119.500008" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="119.500008" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-119.500008" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="112.970039" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="-0.5" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowYaw" mute="false" unit="degree">
|
||||
<Key value="70.2246094" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="70.2246094" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="70.2246094" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="70.2246094" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowRoll" mute="false" unit="degree">
|
||||
<Key value="29.7070332" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="29.7070332" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="29.7070332" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="29.7070332" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RWristYaw" mute="false" unit="degree">
|
||||
<Key value="1.57964516" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.588752136" frame="101">
|
||||
<Tangent side="right" abscissaParam="32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="199">
|
||||
<Tangent side="left" abscissaParam="-32.6666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="299">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="401">
|
||||
<Tangent side="left" abscissaParam="-34" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
</Animation>
|
208
code/src/Fitbot/app/src/main/res/raw/armraise.qianim
Normal file
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Animation xmlns:editor="http://www.ald.softbankrobotics.com/animation/editor" typeVersion="2.0" editor:fps="25">
|
||||
<editor:clip editor:fps="25" editor:startFrame="0" editor:endFrame="201"/>
|
||||
<ActuatorCurve fps="25" actuator="HeadYaw" mute="false" unit="degree">
|
||||
<Key value="-0.439454377" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HeadPitch" mute="false" unit="degree">
|
||||
<Key value="-12.1289139" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="90.5273514" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-83.5680313" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="90.5273514" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="6.67969275" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-69.7851486" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowRoll" mute="false" unit="degree">
|
||||
<Key value="-0.5" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LWristYaw" mute="false" unit="degree">
|
||||
<Key value="-1.76023543" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.589630966" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipRoll" mute="false" unit="degree">
|
||||
<Key value="-0.26367262" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipPitch" mute="false" unit="degree">
|
||||
<Key value="-1.49414492" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="KneePitch" mute="false" unit="degree">
|
||||
<Key value="0" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="90.5273438" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-85.0710983" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="90.5273438" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="-6.59180212" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-6.59180212" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-6.59180212" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowYaw" mute="false" unit="degree">
|
||||
<Key value="70.2246094" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="70.2246094" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="70.2246094" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowRoll" mute="false" unit="degree">
|
||||
<Key value="0.5" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.681336999" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.5" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RWristYaw" mute="false" unit="degree">
|
||||
<Key value="1.57964516" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.588752136" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
</Animation>
|
1
code/src/Fitbot/app/src/main/res/raw/boxing.qianim
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><Animation xmlns:editor="http://www.aldebaran.com/animation/editor" typeVersion="2.0"/>
|
99
code/src/Fitbot/app/src/main/res/raw/chestpress.qianim
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Animation xmlns:editor="http://www.ald.softbankrobotics.com/animation/editor" typeVersion="2.0" editor:fps="25">
|
||||
<ActuatorCurve fps="25" actuator="LShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="11.4200077" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="58.7350922" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="6.67969275" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-88.3528824" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-88.3528824" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowRoll" mute="false" unit="degree">
|
||||
<Key value="-5.89348125" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-89.5" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LWristYaw" mute="false" unit="degree">
|
||||
<Key value="-1.76023543" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.589630966" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="15.1628304" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="62.0810318" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="-6.59180212" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-6.59180212" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowYaw" mute="false" unit="degree">
|
||||
<Key value="96.1428452" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="96.1428452" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowRoll" mute="false" unit="degree">
|
||||
<Key value="0.5" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="89.5" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RWristYaw" mute="false" unit="degree">
|
||||
<Key value="1.57964516" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.588752136" frame="20">
|
||||
<Tangent side="right" abscissaParam="8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="45">
|
||||
<Tangent side="left" abscissaParam="-8.33333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
</Animation>
|
BIN
code/src/Fitbot/app/src/main/res/raw/good_sound.mp3
Normal file
147
code/src/Fitbot/app/src/main/res/raw/shoulderpress.qianim
Normal file
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Animation xmlns:editor="http://www.ald.softbankrobotics.com/animation/editor" typeVersion="2.0" editor:fps="25">
|
||||
<ActuatorCurve fps="25" actuator="LShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="69.5353165" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-74.2801361" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="69.5353165" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="6.67969275" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="6.67969275" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-69.7851486" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-69.7851486" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowRoll" mute="false" unit="degree">
|
||||
<Key value="-89.5" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.5" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-89.5" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LWristYaw" mute="false" unit="degree">
|
||||
<Key value="-1.76023543" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.76023543" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.589630966" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.589630966" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="68.6139908" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-72.7772903" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="68.6139908" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="-6.59180212" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-6.59180212" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-6.59180212" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowYaw" mute="false" unit="degree">
|
||||
<Key value="96.1428452" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="96.1428452" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="96.1428452" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowRoll" mute="false" unit="degree">
|
||||
<Key value="89.5" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.5" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="89.5" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RWristYaw" mute="false" unit="degree">
|
||||
<Key value="1.57964516" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="1.57964516" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.588752136" frame="0">
|
||||
<Tangent side="right" abscissaParam="9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="29">
|
||||
<Tangent side="left" abscissaParam="-9.66666667" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0.588752136" frame="63">
|
||||
<Tangent side="left" abscissaParam="-11.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
</Animation>
|
99
code/src/Fitbot/app/src/main/res/raw/squat.qianim
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Animation xmlns:editor="http://www.ald.softbankrobotics.com/animation/editor" typeVersion="2.0" editor:fps="25">
|
||||
<ActuatorCurve fps="25" actuator="HeadYaw" mute="false" unit="degree">
|
||||
<Key value="-0.439454377" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.439454377" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HeadPitch" mute="false" unit="degree">
|
||||
<Key value="-12.1289139" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-12.1289139" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="-5.74017525" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="6.67969275" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-2.0873363" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LElbowRoll" mute="false" unit="degree">
|
||||
<Key value="-0.5" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LWristYaw" mute="false" unit="degree">
|
||||
<Key value="-12.7772932" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="LHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.98" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipRoll" mute="false" unit="degree">
|
||||
<Key value="-0.26367262" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-0.26367262" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="HipPitch" mute="false" unit="degree">
|
||||
<Key value="-1.49414492" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-42.3515282" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="-1.49414492" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="KneePitch" mute="false" unit="degree">
|
||||
<Key value="0" frame="0">
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="29.5" frame="100">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
<Tangent side="right" abscissaParam="33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
<Key value="0" frame="200">
|
||||
<Tangent side="left" abscissaParam="-33.3333333" ordinateParam="0" editor:interpType="bezier_auto"/>
|
||||
</Key>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderPitch" mute="false" unit="degree">
|
||||
<Key value="-5.69999981" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RShoulderRoll" mute="false" unit="degree">
|
||||
<Key value="-0.5" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowYaw" mute="false" unit="degree">
|
||||
<Key value="-2.0999999" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RElbowRoll" mute="false" unit="degree">
|
||||
<Key value="0.5" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RWristYaw" mute="false" unit="degree">
|
||||
<Key value="1.57964516" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
<ActuatorCurve fps="25" actuator="RHand" mute="false" unit="dimensionless">
|
||||
<Key value="0.98" frame="0"/>
|
||||
</ActuatorCurve>
|
||||
</Animation>
|
BIN
code/src/Fitbot/app/src/main/res/raw/wrong_sound.wav
Normal file
@@ -9,9 +9,13 @@
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="darkBlue">#1C1C27</color>
|
||||
<color name="midBlue">#24242F</color>
|
||||
<color name="midderBlue">#262630</color>
|
||||
<color name="lightBlue">#2C2C37</color>
|
||||
<color name="invertedBackground">#FFFFFF</color>
|
||||
<color name="invertedTextColor">#000000</color>
|
||||
<color name="invertedIconTint">#000000</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
<color name="shimmerStartColor">#EFEFEF</color>
|
||||
<color name="shimmerEndColor">#ADADAD</color>
|
||||
</resources>
|
||||
|
||||
|
4
code/src/Fitbot/app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="skipButtonFitness" type="id" />
|
||||
</resources>
|
@@ -23,4 +23,11 @@
|
||||
<string name="voltooid">U heeft de oefeningen voltooid! \n Druk op start om nog een sessie te beginnen</string>
|
||||
<string name="score">Score:</string>
|
||||
|
||||
<string name="title">Title</string>
|
||||
|
||||
<string name="description">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.</string>
|
||||
<string name="descriptionTitle">Description</string>
|
||||
|
||||
<string name="exerciseDesc">Exercise description</string>
|
||||
<string name="exerciseTitle">Exercise title</string>
|
||||
</resources>
|
@@ -25,10 +25,22 @@
|
||||
<item name="android:padding">6dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextStyleDesc">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textColor">#FFFFFF</item>
|
||||
<item name= "android:textStyle">bold</item>
|
||||
<item name="android:padding">6dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextStyleTitle">
|
||||
<item name="android:textSize">50sp</item>
|
||||
<item name="android:textSize">40sp</item>
|
||||
<item name="android:textColor">#D3D3D3</item>
|
||||
<item name= "android:textStyle">bold</item>
|
||||
<item name="android:padding">6dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressCircle" parent="Widget.AppCompat.ProgressBar">
|
||||
<item name="android:indeterminateDrawable">@drawable/progress_circle</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@@ -1,6 +1,5 @@
|
||||
package com.example.fitbot;
|
||||
|
||||
import com.example.fitbot.exercise.EMuscleGroup;
|
||||
import com.example.fitbot.exercise.Exercise;
|
||||
import com.example.fitbot.exercise.ExerciseManager;
|
||||
|
||||
@@ -11,11 +10,11 @@ public class DatabaseFetchingTest {
|
||||
|
||||
@Test
|
||||
public void testDatabaseFetching() {
|
||||
Exercise exercise = ExerciseManager.retrieveExercise();
|
||||
Exercise exercise = ExerciseManager.fetchExerciseFromDatabase();
|
||||
assert exercise != null;
|
||||
System.out.println("\n---------------------------------");
|
||||
System.out.println("Exercise:");
|
||||
System.out.println("Name: " + exercise.title);
|
||||
System.out.println("Name: " + exercise.name);
|
||||
System.out.println("Description: " + exercise.description);
|
||||
System.out.println("Muscle Group: " + exercise.muscleGroup);
|
||||
System.out.println("Image URL: " + exercise.imageUrl);
|
||||
|
@@ -1,37 +0,0 @@
|
||||
package com.example.fitbot;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import com.example.fitbot.util.path.GesturePath;
|
||||
import com.example.fitbot.util.path.PathSegment;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PathSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testPathSegment() {
|
||||
// Test the PathSegment class
|
||||
Vector3f[] vectors = new Vector3f[2];
|
||||
vectors[0] = new Vector3f(0, 0, 0);
|
||||
vectors[1] = new Vector3f(1, 1, 1);
|
||||
GesturePath path = new GesturePath(vectors);
|
||||
PathSegment[] segments = path.getSegments();
|
||||
assertEquals(1, segments.length);
|
||||
assertEquals(new Vector3f(0, 0, 0), segments[0].getStart());
|
||||
assertEquals(new Vector3f(1, 1, 1), segments[0].getEnd());
|
||||
assertEquals(new Vector3f(0.5f, 0.5f, 0.5f), segments[0].interpolate(0.5));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_pathSegmentInterpolation() {
|
||||
Vector3f start = new Vector3f(0, 0, 0);
|
||||
Vector3f end = new Vector3f(1, 1, 1);
|
||||
PathSegment segment = new PathSegment(start, end);
|
||||
assertEquals(new Vector3f(0.5f, 0.5f, 0.5f), segment.interpolate(0.5));
|
||||
}
|
||||
|
||||
|
||||
}
|