Added tuning feature and hand palm action to reset the timer

This commit is contained in:
harine
2026-09-07 19:38:43 +08:00
parent 41b567929a
commit e7cb217de8
18 changed files with 1164 additions and 178 deletions
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/overlay_scrim" />
<corners android:radius="16dp" />
<stroke android:width="2dp" android:color="@color/step_counter_accent" />
</shape>
@@ -72,18 +72,87 @@
android:textColor="@color/white"
android:textSize="16sp"
android:fontFamily="monospace" />
</LinearLayout>
<!-- Live step counter: see the portrait layout's copy of this view for
the full rationale. Same IDs, centered top here too since
landscape's top edge is otherwise clear (buttons moved to the side
column below). -->
<LinearLayout
android:id="@+id/card_step_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="72dp"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/shape_step_counter_card"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- Live step counter, resets to 0 when the bowler returns to a
stationary starting stance mid-recording (see LiveStepDetector). -->
<TextView
android:id="@+id/text_step_count"
android:id="@+id/text_step_count_big"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/step_count_placeholder"
android:text="@string/step_count_big_placeholder"
android:textColor="@color/white"
android:textSize="16sp"
android:textSize="40sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/step_counter_label"
android:textColor="@color/step_counter_accent"
android:textSize="12sp"
android:letterSpacing="0.15"
android:textStyle="bold" />
</LinearLayout>
<!-- Hold-to-reset gesture: see the portrait layout's copy for the full
rationale. Bottom-center of the whole screen here instead of above
switch_pose, since landscape's buttons sit in a side column rather
than a bottom bar. -->
<LinearLayout
android:id="@+id/layout_reset_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@color/overlay_scrim"
android:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progress_hand_raise"
android:layout_width="20dp"
android:layout_height="20dp"
android:indeterminate="false"
android:max="100"
android:progress="0"
app:indicatorSize="20dp"
app:trackThickness="3dp"
app:indicatorColor="@color/step_counter_accent"
app:trackColor="@color/white" />
<TextView
android:id="@+id/text_reset_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/reset_hint_idle"
android:textColor="@color/white"
android:textSize="13sp" />
</LinearLayout>
<!--
@@ -125,6 +194,18 @@
app:layout_constraintBottom_toTopOf="@id/btn_record"
app:layout_constraintEnd_toEndOf="@id/btn_record" />
<!-- Opens ParameterEditorActivity: see the portrait layout's copy
for the full rationale. Above btn_switch_camera in the same
column, only shown while Idle. -->
<Button
android:id="@+id/btn_editor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/editor_button"
app:layout_constraintBottom_toTopOf="@id/btn_switch_camera"
app:layout_constraintEnd_toEndOf="@id/btn_record" />
<!-- Shown instead of the camera UI when CAMERA/RECORD_AUDIO aren't granted -->
<LinearLayout
android:id="@+id/layout_permission_rationale"
@@ -71,18 +71,88 @@
android:textColor="@color/white"
android:textSize="16sp"
android:fontFamily="monospace" />
</LinearLayout>
<!-- Live step counter: large and centered near the top so it's readable
at a glance mid-approach, unlike the small text this replaced.
Visibility tracks recording state the same as
layout_recording_indicator (see BowlingCameraActivity#renderRecordingState). -->
<LinearLayout
android:id="@+id/card_step_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="72dp"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/shape_step_counter_card"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- Live step counter, resets to 0 when the bowler returns to a
stationary starting stance mid-recording (see LiveStepDetector). -->
<TextView
android:id="@+id/text_step_count"
android:id="@+id/text_step_count_big"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/step_count_placeholder"
android:text="@string/step_count_big_placeholder"
android:textColor="@color/white"
android:textSize="16sp"
android:textSize="40sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/step_counter_label"
android:textColor="@color/step_counter_accent"
android:textSize="12sp"
android:letterSpacing="0.15"
android:textStyle="bold" />
</LinearLayout>
<!-- Hold-to-reset gesture: always visible while recording so the
mechanism is discoverable (see LiveStepDetector's class doc for why
this replaced an automatic stillness-based reset), not just once
the bowler is mid-gesture. Text and progress both driven by
CameraViewModel.handRaiseProgress. -->
<LinearLayout
android:id="@+id/layout_reset_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@color/overlay_scrim"
android:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintBottom_toTopOf="@id/switch_pose"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progress_hand_raise"
android:layout_width="20dp"
android:layout_height="20dp"
android:indeterminate="false"
android:max="100"
android:progress="0"
app:indicatorSize="20dp"
app:trackThickness="3dp"
app:indicatorColor="@color/step_counter_accent"
app:trackColor="@color/white" />
<TextView
android:id="@+id/text_reset_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/reset_hint_idle"
android:textColor="@color/white"
android:textSize="13sp" />
</LinearLayout>
<!-- Live pose overlay + baked-in-recording toggle. Only togglable while
@@ -123,6 +193,19 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Opens ParameterEditorActivity: only meaningful between recordings
(see ParameterEditorActivity's class doc; a save is picked up by
the *next* recording), so only shown while Idle. -->
<Button
android:id="@+id/btn_editor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/editor_button"
app:layout_constraintTop_toBottomOf="@id/btn_switch_camera"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Shown instead of the camera UI when CAMERA/RECORD_AUDIO aren't granted -->
<LinearLayout
android:id="@+id/layout_permission_rationale"
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Simple scrollable form for tuning LiveStepDetector's parameters live,
without a rebuild; see ParameterEditorActivity's class doc. Utilitarian
by design (plain TextInputLayout fields, no fancy styling) since this is
a testing/tuning tool, not an end-user settings screen.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/editor_title"
android:textColor="@color/white"
android:textSize="22sp"
android:textStyle="bold"
android:layout_marginBottom="4dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/editor_subtitle"
android:textColor="@color/white"
android:textSize="13sp"
android:layout_marginBottom="20dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/field_min_spacing_ms"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/editor_min_spacing_label"
app:helperText="@string/editor_min_spacing_help"
app:helperTextEnabled="true"
app:boxStrokeColor="@color/step_counter_accent"
app:hintTextColor="@color/step_counter_accent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textColor="@color/white" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/field_min_prominence_ratio"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/editor_prominence_label"
app:helperText="@string/editor_prominence_help"
app:helperTextEnabled="true"
app:boxStrokeColor="@color/step_counter_accent"
app:hintTextColor="@color/step_counter_accent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:textColor="@color/white" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/field_max_frame_jump_ratio"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/editor_jump_ratio_label"
app:helperText="@string/editor_jump_ratio_help"
app:helperTextEnabled="true"
app:boxStrokeColor="@color/step_counter_accent"
app:hintTextColor="@color/step_counter_accent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:textColor="@color/white" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/field_hand_raise_hold_ms"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/editor_hold_ms_label"
app:helperText="@string/editor_hold_ms_help"
app:helperTextEnabled="true"
app:boxStrokeColor="@color/step_counter_accent"
app:hintTextColor="@color/step_counter_accent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textColor="@color/white" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/field_hand_raise_margin_ratio"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/editor_margin_ratio_label"
app:helperText="@string/editor_margin_ratio_help"
app:helperTextEnabled="true"
app:boxStrokeColor="@color/step_counter_accent"
app:hintTextColor="@color/step_counter_accent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:textColor="@color/white" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="28dp">
<Button
android:id="@+id/btn_reset_defaults"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:text="@string/editor_reset_defaults" />
<Button
android:id="@+id/btn_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:text="@string/editor_save" />
</LinearLayout>
<Button
android:id="@+id/btn_editor_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/back" />
</LinearLayout>
</ScrollView>
+1
View File
@@ -13,4 +13,5 @@
<color name="skeleton_joint">#FF00E5FF</color>
<color name="skeleton_bone">#FF76FF03</color>
<color name="overlay_scrim">#99000000</color>
<color name="step_counter_accent">#FF03DAC5</color>
</resources>
+23 -3
View File
@@ -11,11 +11,31 @@
<string name="stop_recording">Stop recording</string>
<string name="switch_camera">Switch camera</string>
<string name="back">Back</string>
<string name="switch_camera_while_recording">Stop recording before switching cameras</string>
<string name="recording_timer_placeholder">00:00</string>
<string name="step_count_placeholder">Step 0</string>
<string name="step_count_format">Step %1$d</string>
<string name="step_count_big_placeholder">0</string>
<string name="step_counter_label">STEPS</string>
<string name="reset_hint_idle">✋ Raise a hand, hold 5s to reset</string>
<string name="reset_hint_holding">Keep holding… %1$d%%</string>
<string name="error_camera_unavailable">Camera unavailable: %1$s</string>
<string name="error_recording_failed">Recording failed: %1$s</string>
<string name="error_pose_detector">Pose detector error: %1$s</string>
<string name="editor_button">Tuning</string>
<!-- Parameter editor screen -->
<string name="editor_title">Step Detection Tuning</string>
<string name="editor_subtitle">Changes apply the next time you start a recording.</string>
<string name="editor_min_spacing_label">Min spacing between steps (ms)</string>
<string name="editor_min_spacing_help">Minimum time between two counted steps on the same foot. Default 300.</string>
<string name="editor_prominence_label">Step prominence ratio</string>
<string name="editor_prominence_help">How far a foot must move (as a fraction of torso size) to count as a step. Default 0.15.</string>
<string name="editor_jump_ratio_label">Max frame jump ratio</string>
<string name="editor_jump_ratio_help">Largest single-frame ankle movement trusted as real motion vs. a tracking glitch, as a fraction of torso size. Default 0.25.</string>
<string name="editor_hold_ms_label">Reset hold duration (ms)</string>
<string name="editor_hold_ms_help">How long a hand must stay raised to trigger a reset. Default 5000.</string>
<string name="editor_margin_ratio_label">Raise height ratio</string>
<string name="editor_margin_ratio_help">How far above the shoulder a wrist must reach to count as raised, as a fraction of torso size. Default 0.05.</string>
<string name="editor_reset_defaults">Reset to defaults</string>
<string name="editor_save">Save</string>
<string name="editor_saved_toast">Settings saved</string>
<string name="editor_invalid_value_toast">Enter a valid number for every field</string>
</resources>