Files
PinPoint/app/src/main/java/com/example/jnicpp/bowling/StepCounterUiController.kt
T

86 lines
3.2 KiB
Kotlin
Raw Normal View History

/**
* @file StepCounterUiController.kt
2026-09-09 20:09:18 +08:00
* @brief Rendering for the live step-counter card.
*/
package com.example.jnicpp.bowling
import android.view.View
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.example.jnicpp.R
/**
2026-09-09 20:09:18 +08:00
* @brief Owns rendering for [BowlingCameraActivity]'s step-counter card.
*
* @param cardStepCounter The step-counter card container view.
* @param textStepCountBig The large step-count number TextView.
* @param textStepCounterLabel The small "STEPS" label below the count,
* repurposed to flag when the latest step wasn't corroborated by
* [PosePhaseDetector] -- see [renderStepCount]'s `latestPoseConfirmed`.
*/
class StepCounterUiController(
private val cardStepCounter: View,
2026-09-09 22:48:06 +08:00
private val textStepCountBig: TextView,
private val textStepCounterLabel: TextView,
) {
// Last step count rendered, so pulse() in renderStepCount only plays
2026-09-09 20:09:18 +08:00
// when a new step actually pushed the count up.
private var lastRenderedStepCount = 0
/**
2026-09-09 20:09:18 +08:00
* @brief Shows or hides the step counter card.
* @param visible true to reveal (recording in progress), false to hide.
*/
fun setVisible(visible: Boolean) {
2026-09-09 20:09:18 +08:00
cardStepCounter.visibility = if (visible) View.VISIBLE else View.GONE
}
/** @brief Clears pulse-tracking state; call whenever a new recording starts. */
fun resetTracking() {
lastRenderedStepCount = 0
}
/**
* @brief Renders the current step count, pulsing the card if a new step was just confirmed.
*
* Never withholds or delays a step because of [latestPoseConfirmed] --
* the ankle-peak count is always trusted (see [StepEvent.poseConfirmed]'s
* doc for why); this only swaps the small label below the number to flag
* a disagreement for whoever's testing/tuning detection to notice.
*
* @param stepCount Total steps counted so far in the current attempt.
* @param latestPoseConfirmed Whether the most recently counted step (if
* any) was corroborated by [PosePhaseDetector] at the time it was
* counted; ignored when [stepCount] is 0.
*/
fun renderStepCount(stepCount: Int, latestPoseConfirmed: Boolean = true) {
textStepCountBig.text = stepCount.toString()
if (stepCount > lastRenderedStepCount) {
pulse()
}
lastRenderedStepCount = stepCount
val flagged = stepCount > 0 && !latestPoseConfirmed
textStepCounterLabel.text = textStepCounterLabel.context.getString(
if (flagged) R.string.step_counter_label_unconfirmed else R.string.step_counter_label
)
textStepCounterLabel.setTextColor(
ContextCompat.getColor(
textStepCounterLabel.context,
if (flagged) R.color.recording_red else R.color.step_counter_accent,
)
)
}
/** @brief Briefly scales the step counter up and back down, drawing the eye to a newly confirmed step. */
private fun pulse() {
cardStepCounter.animate()
.scaleX(1.15f).scaleY(1.15f)
.setDuration(80)
.withEndAction {
cardStepCounter.animate().scaleX(1f).scaleY(1f).setDuration(120).start()
}
.start()
}
}