/** * @file StepCounterUiController.kt * @brief Rendering for the live step-counter card and hold-to-reset gesture indicator. */ package com.example.jnicpp.bowling import android.content.Context import android.view.View import android.widget.TextView import com.example.jnicpp.R import com.google.android.material.progressindicator.CircularProgressIndicator /** * @brief Owns rendering for [BowlingCameraActivity]'s step-counter card and * hold-to-reset gesture indicator, so the Activity's job stays * limited to wiring [CameraViewModel] state into this controller * rather than holding view-rendering logic itself. * * @param context Used only for string resource lookups. * @param cardStepCounter The step-counter card container view. * @param textStepCountBig The large step-count number TextView. * @param layoutResetHint The hold-to-reset hint row container view. * @param progressHandRaise The circular hold-progress indicator. * @param textResetHint The hold-to-reset hint text. */ class StepCounterUiController( private val context: Context, private val cardStepCounter: View, private val textStepCountBig: TextView, private val layoutResetHint: View, private val progressHandRaise: CircularProgressIndicator, private val textResetHint: TextView ) { // Last step count rendered, so pulse() in renderStepCount only plays // when a new step actually pushed the count up, not on every // stepEvents emission -- a reset back to zero shouldn't visually "pop". private var lastRenderedStepCount = 0 /** * @brief Shows or hides the step counter and reset-hint views together. * @param visible true to reveal both (recording in progress), false to hide them. */ fun setVisible(visible: Boolean) { val visibility = if (visible) View.VISIBLE else View.GONE cardStepCounter.visibility = visibility layoutResetHint.visibility = visibility } /** @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. * @param stepCount Total steps counted so far in the current attempt. */ fun renderStepCount(stepCount: Int) { textStepCountBig.text = stepCount.toString() if (stepCount > lastRenderedStepCount) { pulse() } lastRenderedStepCount = stepCount } /** * @brief Reflects the hold-to-reset gesture's progress onto the * circular indicator and hint text. * @param progress Current hold progress, from 0 (not raised) to 1 (reset just fired). */ fun renderHandRaiseProgress(progress: Float) { progressHandRaise.progress = (progress * 100).toInt() textResetHint.text = if (progress <= 0f) { context.getString(R.string.reset_hint_idle) } else { context.getString(R.string.reset_hint_holding, (progress * 100).toInt()) } } /** @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() } }