Added tuning feature and hand palm action to reset the timer
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
package com.example.jnicpp.bowling
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
@@ -61,6 +62,11 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
// doc. Open only while a recording is in progress.
|
||||
private lateinit var debugSessionLogger: DebugSessionLogger
|
||||
|
||||
// Rendering for the step-counter card and hold-to-reset indicator --
|
||||
// see StepCounterUiController's class doc for why this isn't just
|
||||
// inline here.
|
||||
private lateinit var stepCounterUi: StepCounterUiController
|
||||
|
||||
private val permissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { _ ->
|
||||
val granted = CameraPermissions.allGranted(this)
|
||||
@@ -87,6 +93,14 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
cameraXController = CameraXController(applicationContext, cameraExecutor)
|
||||
debugSessionLogger = DebugSessionLogger(applicationContext)
|
||||
stepCounterUi = StepCounterUiController(
|
||||
context = this,
|
||||
cardStepCounter = binding.cardStepCounter,
|
||||
textStepCountBig = binding.textStepCountBig,
|
||||
layoutResetHint = binding.layoutResetHint,
|
||||
progressHandRaise = binding.progressHandRaise,
|
||||
textResetHint = binding.textResetHint
|
||||
)
|
||||
|
||||
binding.btnGrantPermissions.setOnClickListener {
|
||||
permissionLauncher.launch(CameraPermissions.REQUIRED)
|
||||
@@ -95,6 +109,7 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
binding.switchPose.setOnCheckedChangeListener { _, isChecked -> viewModel.onPoseToggled(isChecked) }
|
||||
binding.btnSwitchCamera.setOnClickListener { onSwitchCameraClicked() }
|
||||
binding.btnBack.setOnClickListener { finish() }
|
||||
binding.btnEditor.setOnClickListener { startActivity(Intent(this, ParameterEditorActivity::class.java)) }
|
||||
|
||||
observeViewModel()
|
||||
|
||||
@@ -132,11 +147,21 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Handles the switch-camera button: toggles [lensFacing] and rebinds, unless a recording is in progress. */
|
||||
/**
|
||||
* @brief Handles the switch-camera button: toggles [lensFacing] and rebinds.
|
||||
*
|
||||
* If a recording is in progress, stops it first (same effect as
|
||||
* tapping Stop Recording) rather than blocking the switch outright --
|
||||
* a debug/testing convenience so trying both cameras doesn't need a
|
||||
* separate stop first. [CameraXController.Callback.onRecordingFinalized]
|
||||
* still fires asynchronously and saves the take normally, up to the
|
||||
* point it was stopped; only the new camera's stream starts fresh,
|
||||
* with no live pose/step-count carried over, same as ending any other
|
||||
* take.
|
||||
*/
|
||||
private fun onSwitchCameraClicked() {
|
||||
if (cameraXController.isRecording) {
|
||||
Toast.makeText(this, R.string.switch_camera_while_recording, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
cameraXController.stopRecording()
|
||||
}
|
||||
lensFacing = if (lensFacing == CameraSelector.LENS_FACING_BACK) {
|
||||
CameraSelector.LENS_FACING_FRONT
|
||||
@@ -178,12 +203,15 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
}
|
||||
launch {
|
||||
viewModel.stepEvents.collect { events ->
|
||||
binding.textStepCount.text = getString(R.string.step_count_format, events.size)
|
||||
stepCounterUi.renderStepCount(events.size)
|
||||
if (events.isNotEmpty()) {
|
||||
Log.d(TAG, "Step ${events.size}: ${events.last()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
viewModel.handRaiseProgress.collect { progress -> stepCounterUi.renderHandRaiseProgress(progress) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,23 +239,32 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
when (state) {
|
||||
is CameraViewModel.RecordingState.Idle -> {
|
||||
binding.layoutRecordingIndicator.visibility = View.GONE
|
||||
stepCounterUi.setVisible(false)
|
||||
binding.btnRecord.isEnabled = true
|
||||
binding.btnRecord.setText(R.string.record)
|
||||
// Pose mode can only be changed between recordings, not
|
||||
// mid-flight -- see setPoseDetectionEnabled()'s doc comment.
|
||||
binding.switchPose.isEnabled = true
|
||||
stepCounterUi.resetTracking()
|
||||
// A tuning change only takes effect on the *next* recording
|
||||
// (see ParameterEditorActivity's class doc), so only offer
|
||||
// it while there isn't one already in progress.
|
||||
binding.btnEditor.visibility = View.VISIBLE
|
||||
}
|
||||
is CameraViewModel.RecordingState.Starting -> {
|
||||
// Can't stop a recording that hasn't started yet, and pose
|
||||
// mode for it is already locked in.
|
||||
binding.btnRecord.isEnabled = false
|
||||
binding.switchPose.isEnabled = false
|
||||
binding.btnEditor.visibility = View.GONE
|
||||
}
|
||||
is CameraViewModel.RecordingState.Recording -> {
|
||||
binding.btnRecord.isEnabled = true
|
||||
binding.btnRecord.setText(R.string.stop_recording)
|
||||
binding.switchPose.isEnabled = false
|
||||
binding.layoutRecordingIndicator.visibility = View.VISIBLE
|
||||
stepCounterUi.setVisible(true)
|
||||
binding.btnEditor.visibility = View.GONE
|
||||
val minutes = state.elapsedSeconds / 60
|
||||
val seconds = state.elapsedSeconds % 60
|
||||
binding.textTimer.text = String.format(Locale.US, "%02d:%02d", minutes, seconds)
|
||||
@@ -320,7 +357,12 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
|
||||
viewModel.onPoseFrameUpdated(result.landmarks, result.angles)
|
||||
|
||||
val frameTimestampMs = System.currentTimeMillis()
|
||||
debugSessionLogger.log(result.landmarks, frameTimestampMs, viewModel.stepEvents.value.size)
|
||||
debugSessionLogger.log(
|
||||
result.landmarks,
|
||||
frameTimestampMs,
|
||||
viewModel.stepEvents.value.size,
|
||||
viewModel.handRaiseProgress.value
|
||||
)
|
||||
|
||||
if (frameTimestampMs - lastLandmarkLogMs >= 1000) {
|
||||
lastLandmarkLogMs = frameTimestampMs
|
||||
|
||||
Reference in New Issue
Block a user