2024-06-05 13:56:33 +02:00
4 changed files with 42 additions and 49 deletions

View File

@@ -55,6 +55,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
private static String exerciseVideoUrl;
private Animate animate;
private VideoView videoView;
private QiContext qiContext;
private final Object lock = new Object();
@@ -90,9 +91,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
progressCircle = findViewById(R.id.progressCircle);
progressText = findViewById(R.id.progressText);
progressCircle.setMax(maxProgress);
updateProgress();
// Set color of loading circle
ProgressBar loadingCircle = findViewById(R.id.loadingCircle);
@@ -133,22 +132,6 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
motionProcessor.setRecording(true, 10);
motionProcessor.startListening();
if ( videoView.isPlaying() )
{
Animation animationarmraise = AnimationBuilder.with(qiContext) // Create the builder with the context.
.withResources(R.raw.armraise) // Set the animation resource.
.build(); // Build the animation.
animate = AnimateBuilder.with(qiContext) // Create the builder with the context.
.withAnimation(animationarmraise) // Set the animation.
.build(); // Build the animate action.
Future<Void> animateFuture = animate.async().run();
}
else
{
Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
}
}, (n) -> {
int randomMessageIndex = (int) Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
@@ -186,6 +169,25 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
ProgressBar loadingCircle = findViewById(R.id.loadingCircle);
loadingCircle.setVisibility(View.GONE);
if ( videoView.isPlaying() )
{
QiContext qiContext = null;
Animation animationarmraise = AnimationBuilder.with(qiContext) // Create the builder with the context.
.withResources(R.raw.armraise) // Set the animation resource.
.build(); // Build the animation.
animate = AnimateBuilder.with(qiContext) // Create the builder with the context.
.withAnimation(animationarmraise) // Set the animation.
.build(); // Build the animate action.
Future<Void> animateFuture = animate.async().run();
}
else
{
Log.e("FitnessActivity", "VideoView is null. Check your layout XML.");
}
return true;
}
return false;
@@ -266,7 +268,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
dialog.show();
}
public void incrementProgress(View view) {
public void incrementProgress() {
if (progress < maxProgress) {
progress++;
triggerColorBurst(true);
@@ -279,15 +281,12 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
progressText.setText(progress + "/" + maxProgress);
}
private void triggerColorBurst(boolean isGoodRep) {
public void triggerColorBurst(boolean isGoodRep) {
if (isGoodRep) {
progressCircle.setProgressDrawable(ContextCompat.getDrawable(this, R.drawable.progress_circle_good));
new MediaPlayer().create(this, R.raw.good_sound).start();
} else {
progressCircle.setProgressDrawable(ContextCompat.getDrawable(this, R.drawable.progress_circle_bad));
new MediaPlayer().create(this, R.raw.wrong_sound).start();
}
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(500); // Burst duration

View File

@@ -28,7 +28,7 @@ public class InputProcessor {
private Vector3f[][] targetRotationVectorPaths; // Target path of the motion data
private final float sampleRate; // The sample rate of the motion sensor
private float exerciseDurationInSeconds;
private float exerciseRepetitionDurationInSeconds = 0.0f;
private int repetitionsRemaining = 0;
private int exercisesRemaining = 0;
private float exerciseScore = 0.0F;
@@ -120,7 +120,7 @@ public class InputProcessor {
this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getAngleVectors().length];
this.targetRotationVectorPaths[0] = exercise.leftPath.getAngleVectors();
this.targetRotationVectorPaths[1] = exercise.rightPath.getAngleVectors();
this.exerciseDurationInSeconds = exercise.exerciseTimeInSeconds;
this.exerciseRepetitionDurationInSeconds = exercise.exerciseTimeInSeconds;
this.secondsPassed = 0.0D;
this.lastTime = System.currentTimeMillis();
}
@@ -130,15 +130,14 @@ public class InputProcessor {
*/
public void onAdequateRepetition() {
ExerciseManager.TOTAL_REPETITIONS_PERFORMED++;
// TODO: Add animation for correct repetition
new FitnessActivity().incrementProgress();
}
/**
* Method that is called whenever the user performs a bad repetition.
*/
public void onInadequateRepetition() {
// TODO: Add animation for wrong repetition
new FitnessActivity().triggerColorBurst(false);
}
/**
@@ -170,7 +169,7 @@ public class InputProcessor {
public boolean hasFinished() {
return this.recordingMovement ?
(this.secondsPassed >= this.recordingDurationInSeconds) :
(this.secondsPassed >= this.exerciseDurationInSeconds);
(this.secondsPassed >= this.exerciseRepetitionDurationInSeconds);
}
/**
@@ -367,6 +366,10 @@ public class InputProcessor {
// Ensure the sensor ID is within the bounds of the array
if (sensorId < 0 || sensorId >= selfRotationVectorPaths.length)
return 0.0d;
// Calculate the index of the rotational vector where we're currently at
int targetRotationIdx =
/*
// Index of the current rotation vector
@@ -394,10 +397,10 @@ public class InputProcessor {
*/
public double getAverageError(int sensorId) {
double error = 0;
for (int i = 0; i < this.exerciseDurationInSeconds; i++) {
for (int i = 0; i < this.exerciseRepetitionDurationInSeconds; i++) {
error += getError(sensorId, i);
}
return error / this.exerciseDurationInSeconds;
return error / this.exerciseRepetitionDurationInSeconds;
}
public float secondsPassed() {

View File

@@ -77,7 +77,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/title"
android:text="@string/exerciseTitle"
android:textAlignment="center" />
<LinearLayout
@@ -93,20 +93,11 @@
style="@style/TextStyleDesc"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/context"
android:text="@string/exerciseDesc"
android:textAlignment="center" />
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/progressText"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:onClick="incrementProgress"
android:text="Increment" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -123,7 +114,7 @@
android:layout_gravity="center"
android:indeterminate="false"
android:max="10"
android:progress="10"
android:progress="0"
android:progressDrawable="@drawable/progress_circle" />
<TextView

View File

@@ -24,8 +24,8 @@
<string name="score">Score:</string>
<string name="title">Title</string>
<string name="context">ContextContextContext</string>
<string name="exerciseDesc">Exercise description</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="exerciseTitle">Exercise title</string>
</resources>