Add live per-step form feedback based on joint angles

Extends pose angle tracking with knee bend (hip-knee-ankle), then uses
it alongside the existing elbow/shoulder angles in a new
PoseStageAdvisor to give a short live cue for whichever step of the
approach is in progress: push-away on step 2, downswing on step 3,
backswing on step 4, and knee-bend/arm-extension on the final step.
Thresholds are starting defaults, not measured coaching data, and are
expected to be retuned against real approach footage.

Also fixes a layout bug found while testing this live: the new
feedback text was chained via ConstraintLayout's toBottomOf to the
step banner above it, so whenever that banner was hidden (GONE) the
feedback text rendered at its collapsed zero-height position instead
of staying put, landing on top of the recording indicator. Both now
sit in a plain vertical LinearLayout, which collapses GONE children
correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
midnight-masala
2026-09-07 21:22:11 +08:00
co-authored by Claude Sonnet 5
parent 2bb7c6ca7b
commit e7a9c2b140
7 changed files with 230 additions and 38 deletions
@@ -86,29 +86,58 @@
android:textStyle="bold" />
</LinearLayout>
<!-- Live "you're in your final position" banner, shown once the 5th
(final) foot-plant of the current attempt is detected (see
BowlingCameraActivity's stepEvents collector), cleared automatically
whenever the step count resets for the next attempt. Centered
horizontally regardless of orientation, anchored below the top
corner buttons so it never overlaps them. -->
<TextView
android:id="@+id/text_final_position"
<!-- Groups the step/final-position banner and the per-step form cue into
one centered vertical stack, anchored below the top corner buttons.
A LinearLayout (rather than each TextView chained to the other via
ConstraintLayout's toBottomOf) so a GONE child collapses cleanly.
Chaining directly had text_pose_feedback render at text_final_position's
collapsed (zero-height) position whenever the latter was hidden,
landing on top of the recording indicator instead of staying put. -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/overlay_scrim"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
android:text="@string/final_position_reached"
android:textColor="@color/final_position_highlight"
android:textSize="22sp"
android:textStyle="bold"
android:visibility="gone"
tools:visibility="visible"
android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintTop_toBottomOf="@id/btn_switch_camera"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />
android:layout_marginTop="16dp">
<!-- Live "you're in your final position" banner, shown once the 5th
(final) foot-plant of the current attempt is detected (see
BowlingCameraActivity's stepEvents collector), cleared
automatically whenever the step count resets for the next attempt. -->
<TextView
android:id="@+id/text_final_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/overlay_scrim"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
android:text="@string/final_position_reached"
android:textColor="@color/final_position_highlight"
android:textSize="22sp"
android:textStyle="bold"
android:visibility="gone"
tools:visibility="visible" />
<!-- Live per-step form cue from PoseStageAdvisor (e.g. "Good
push-away", "Bend your sliding knee more"), see
CameraViewModel.poseStageFeedback. -->
<TextView
android:id="@+id/text_pose_feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@color/overlay_scrim"
android:paddingHorizontal="16dp"
android:paddingVertical="6dp"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="gone"
tools:text="Good push-away"
tools:visibility="visible" />
</LinearLayout>
<!--
Mirrored onto the start edge at the same vertical center as btn_record